>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg=\"\",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg=\"incorrect header check\",r.mode=30;break}if(8!=(15&u)){e.msg=\"unknown compression method\",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg=\"invalid window size\",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg=\"invalid block type\",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg=\"invalid stored block lengths\",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg=\"invalid code lengths set\",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg=\"invalid bit length repeat\",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg=\"invalid bit length repeat\",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg=\"invalid code -- missing end-of-block\",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg=\"invalid literal/lengths set\",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg=\"invalid distances set\",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg=\"invalid literal/length code\",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg=\"invalid distance code\",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg=\"invalid distance too far back\",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg=\"invalid distance too far back\",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{\"../utils/common\":41}],53:[function(e,t,r){\"use strict\";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg=\"\",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){\"use strict\";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i=\"[object process]\"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage(\"\",\"*\"),r.onmessage=t,e}}()?(a=\"setImmediate$\"+Math.random()+\"$\",r.addEventListener?r.addEventListener(\"message\",d,!1):r.attachEvent(\"onmessage\",d),function(e){r.postMessage(a+e,\"*\")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&\"onreadystatechange\"in l.createElement(\"script\")?(s=l.documentElement,function(e){var t=l.createElement(\"script\");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){\"function\"!=typeof e&&(e=new Function(\"\"+e));for(var t=new Array(arguments.length-1),r=0;r arr.length) len = arr.length;\n for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n return arr2;\n}\nmodule.exports = _arrayLikeToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "var arrayLikeToArray = require(\"./arrayLikeToArray.js\");\nfunction _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}\nmodule.exports = _unsupportedIterableToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "function _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\nmodule.exports = _nonIterableRest, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "var arrayWithHoles = require(\"./arrayWithHoles.js\");\nvar iterableToArrayLimit = require(\"./iterableToArrayLimit.js\");\nvar unsupportedIterableToArray = require(\"./unsupportedIterableToArray.js\");\nvar nonIterableRest = require(\"./nonIterableRest.js\");\nfunction _slicedToArray(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || unsupportedIterableToArray(arr, i) || nonIterableRest();\n}\nmodule.exports = _slicedToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n}\nmodule.exports = _classCallCheck, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "function _typeof(o) {\n \"@babel/helpers - typeof\";\n\n return (module.exports = _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) {\n return typeof o;\n } : function (o) {\n return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports), _typeof(o);\n}\nmodule.exports = _typeof, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "var _typeof = require(\"./typeof.js\")[\"default\"];\nfunction toPrimitive(t, r) {\n if (\"object\" != _typeof(t) || !t) return t;\n var e = t[Symbol.toPrimitive];\n if (void 0 !== e) {\n var i = e.call(t, r || \"default\");\n if (\"object\" != _typeof(i)) return i;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (\"string\" === r ? String : Number)(t);\n}\nmodule.exports = toPrimitive, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "var _typeof = require(\"./typeof.js\")[\"default\"];\nvar toPrimitive = require(\"./toPrimitive.js\");\nfunction toPropertyKey(t) {\n var i = toPrimitive(t, \"string\");\n return \"symbol\" == _typeof(i) ? i : String(i);\n}\nmodule.exports = toPropertyKey, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "var toPropertyKey = require(\"./toPropertyKey.js\");\nfunction _defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor);\n }\n}\nfunction _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n Object.defineProperty(Constructor, \"prototype\", {\n writable: false\n });\n return Constructor;\n}\nmodule.exports = _createClass, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;", "(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@babel/runtime/helpers/slicedToArray'), require('@babel/runtime/helpers/classCallCheck'), require('@babel/runtime/helpers/createClass')) :\n typeof define === 'function' && define.amd ? define(['exports', '@babel/runtime/helpers/slicedToArray', '@babel/runtime/helpers/classCallCheck', '@babel/runtime/helpers/createClass'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.automationEvents = {}, global._slicedToArray, global._classCallCheck, global._createClass));\n})(this, (function (exports, _slicedToArray, _classCallCheck, _createClass) { 'use strict';\n\n var createExtendedExponentialRampToValueAutomationEvent = function createExtendedExponentialRampToValueAutomationEvent(value, endTime, insertTime) {\n return {\n endTime: endTime,\n insertTime: insertTime,\n type: 'exponentialRampToValue',\n value: value\n };\n };\n\n var createExtendedLinearRampToValueAutomationEvent = function createExtendedLinearRampToValueAutomationEvent(value, endTime, insertTime) {\n return {\n endTime: endTime,\n insertTime: insertTime,\n type: 'linearRampToValue',\n value: value\n };\n };\n\n var createSetValueAutomationEvent = function createSetValueAutomationEvent(value, startTime) {\n return {\n startTime: startTime,\n type: 'setValue',\n value: value\n };\n };\n\n var createSetValueCurveAutomationEvent = function createSetValueCurveAutomationEvent(values, startTime, duration) {\n return {\n duration: duration,\n startTime: startTime,\n type: 'setValueCurve',\n values: values\n };\n };\n\n var getTargetValueAtTime = function getTargetValueAtTime(time, valueAtStartTime, _ref) {\n var startTime = _ref.startTime,\n target = _ref.target,\n timeConstant = _ref.timeConstant;\n return target + (valueAtStartTime - target) * Math.exp((startTime - time) / timeConstant);\n };\n\n var isExponentialRampToValueAutomationEvent = function isExponentialRampToValueAutomationEvent(automationEvent) {\n return automationEvent.type === 'exponentialRampToValue';\n };\n\n var isLinearRampToValueAutomationEvent = function isLinearRampToValueAutomationEvent(automationEvent) {\n return automationEvent.type === 'linearRampToValue';\n };\n\n var isAnyRampToValueAutomationEvent = function isAnyRampToValueAutomationEvent(automationEvent) {\n return isExponentialRampToValueAutomationEvent(automationEvent) || isLinearRampToValueAutomationEvent(automationEvent);\n };\n\n var isSetValueAutomationEvent = function isSetValueAutomationEvent(automationEvent) {\n return automationEvent.type === 'setValue';\n };\n\n var isSetValueCurveAutomationEvent = function isSetValueCurveAutomationEvent(automationEvent) {\n return automationEvent.type === 'setValueCurve';\n };\n\n var getValueOfAutomationEventAtIndexAtTime = function getValueOfAutomationEventAtIndexAtTime(automationEvents, index, time, defaultValue) {\n var automationEvent = automationEvents[index];\n return automationEvent === undefined ? defaultValue : isAnyRampToValueAutomationEvent(automationEvent) || isSetValueAutomationEvent(automationEvent) ? automationEvent.value : isSetValueCurveAutomationEvent(automationEvent) ? automationEvent.values[automationEvent.values.length - 1] : getTargetValueAtTime(time, getValueOfAutomationEventAtIndexAtTime(automationEvents, index - 1, automationEvent.startTime, defaultValue), automationEvent);\n };\n\n var getEndTimeAndValueOfPreviousAutomationEvent = function getEndTimeAndValueOfPreviousAutomationEvent(automationEvents, index, currentAutomationEvent, nextAutomationEvent, defaultValue) {\n return currentAutomationEvent === undefined ? [nextAutomationEvent.insertTime, defaultValue] : isAnyRampToValueAutomationEvent(currentAutomationEvent) ? [currentAutomationEvent.endTime, currentAutomationEvent.value] : isSetValueAutomationEvent(currentAutomationEvent) ? [currentAutomationEvent.startTime, currentAutomationEvent.value] : isSetValueCurveAutomationEvent(currentAutomationEvent) ? [currentAutomationEvent.startTime + currentAutomationEvent.duration, currentAutomationEvent.values[currentAutomationEvent.values.length - 1]] : [currentAutomationEvent.startTime, getValueOfAutomationEventAtIndexAtTime(automationEvents, index - 1, currentAutomationEvent.startTime, defaultValue)];\n };\n\n var isCancelAndHoldAutomationEvent = function isCancelAndHoldAutomationEvent(automationEvent) {\n return automationEvent.type === 'cancelAndHold';\n };\n\n var isCancelScheduledValuesAutomationEvent = function isCancelScheduledValuesAutomationEvent(automationEvent) {\n return automationEvent.type === 'cancelScheduledValues';\n };\n\n var getEventTime = function getEventTime(automationEvent) {\n if (isCancelAndHoldAutomationEvent(automationEvent) || isCancelScheduledValuesAutomationEvent(automationEvent)) {\n return automationEvent.cancelTime;\n }\n if (isExponentialRampToValueAutomationEvent(automationEvent) || isLinearRampToValueAutomationEvent(automationEvent)) {\n return automationEvent.endTime;\n }\n return automationEvent.startTime;\n };\n\n var getExponentialRampValueAtTime = function getExponentialRampValueAtTime(time, startTime, valueAtStartTime, _ref) {\n var endTime = _ref.endTime,\n value = _ref.value;\n if (valueAtStartTime === value) {\n return value;\n }\n if (0 < valueAtStartTime && 0 < value || valueAtStartTime < 0 && value < 0) {\n return valueAtStartTime * Math.pow(value / valueAtStartTime, (time - startTime) / (endTime - startTime));\n }\n return 0;\n };\n\n var getLinearRampValueAtTime = function getLinearRampValueAtTime(time, startTime, valueAtStartTime, _ref) {\n var endTime = _ref.endTime,\n value = _ref.value;\n return valueAtStartTime + (time - startTime) / (endTime - startTime) * (value - valueAtStartTime);\n };\n\n var interpolateValue = function interpolateValue(values, theoreticIndex) {\n var lowerIndex = Math.floor(theoreticIndex);\n var upperIndex = Math.ceil(theoreticIndex);\n if (lowerIndex === upperIndex) {\n return values[lowerIndex];\n }\n return (1 - (theoreticIndex - lowerIndex)) * values[lowerIndex] + (1 - (upperIndex - theoreticIndex)) * values[upperIndex];\n };\n\n var getValueCurveValueAtTime = function getValueCurveValueAtTime(time, _ref) {\n var duration = _ref.duration,\n startTime = _ref.startTime,\n values = _ref.values;\n var theoreticIndex = (time - startTime) / duration * (values.length - 1);\n return interpolateValue(values, theoreticIndex);\n };\n\n var isSetTargetAutomationEvent = function isSetTargetAutomationEvent(automationEvent) {\n return automationEvent.type === 'setTarget';\n };\n\n var AutomationEventList = /*#__PURE__*/function (_Symbol$iterator) {\n function AutomationEventList(defaultValue) {\n _classCallCheck(this, AutomationEventList);\n this._automationEvents = [];\n this._currenTime = 0;\n this._defaultValue = defaultValue;\n }\n _createClass(AutomationEventList, [{\n key: _Symbol$iterator,\n value: function value() {\n return this._automationEvents[Symbol.iterator]();\n }\n }, {\n key: \"add\",\n value: function add(automationEvent) {\n var eventTime = getEventTime(automationEvent);\n if (isCancelAndHoldAutomationEvent(automationEvent) || isCancelScheduledValuesAutomationEvent(automationEvent)) {\n var index = this._automationEvents.findIndex(function (currentAutomationEvent) {\n if (isCancelScheduledValuesAutomationEvent(automationEvent) && isSetValueCurveAutomationEvent(currentAutomationEvent)) {\n return currentAutomationEvent.startTime + currentAutomationEvent.duration >= eventTime;\n }\n return getEventTime(currentAutomationEvent) >= eventTime;\n });\n var removedAutomationEvent = this._automationEvents[index];\n if (index !== -1) {\n this._automationEvents = this._automationEvents.slice(0, index);\n }\n if (isCancelAndHoldAutomationEvent(automationEvent)) {\n var lastAutomationEvent = this._automationEvents[this._automationEvents.length - 1];\n if (removedAutomationEvent !== undefined && isAnyRampToValueAutomationEvent(removedAutomationEvent)) {\n if (lastAutomationEvent !== undefined && isSetTargetAutomationEvent(lastAutomationEvent)) {\n throw new Error('The internal list is malformed.');\n }\n var startTime = lastAutomationEvent === undefined ? removedAutomationEvent.insertTime : isSetValueCurveAutomationEvent(lastAutomationEvent) ? lastAutomationEvent.startTime + lastAutomationEvent.duration : getEventTime(lastAutomationEvent);\n var startValue = lastAutomationEvent === undefined ? this._defaultValue : isSetValueCurveAutomationEvent(lastAutomationEvent) ? lastAutomationEvent.values[lastAutomationEvent.values.length - 1] : lastAutomationEvent.value;\n var value = isExponentialRampToValueAutomationEvent(removedAutomationEvent) ? getExponentialRampValueAtTime(eventTime, startTime, startValue, removedAutomationEvent) : getLinearRampValueAtTime(eventTime, startTime, startValue, removedAutomationEvent);\n var truncatedAutomationEvent = isExponentialRampToValueAutomationEvent(removedAutomationEvent) ? createExtendedExponentialRampToValueAutomationEvent(value, eventTime, this._currenTime) : createExtendedLinearRampToValueAutomationEvent(value, eventTime, this._currenTime);\n this._automationEvents.push(truncatedAutomationEvent);\n }\n if (lastAutomationEvent !== undefined && isSetTargetAutomationEvent(lastAutomationEvent)) {\n this._automationEvents.push(createSetValueAutomationEvent(this.getValue(eventTime), eventTime));\n }\n if (lastAutomationEvent !== undefined && isSetValueCurveAutomationEvent(lastAutomationEvent) && lastAutomationEvent.startTime + lastAutomationEvent.duration > eventTime) {\n var duration = eventTime - lastAutomationEvent.startTime;\n var ratio = (lastAutomationEvent.values.length - 1) / lastAutomationEvent.duration;\n var length = Math.max(2, 1 + Math.ceil(duration * ratio));\n var fraction = duration / (length - 1) * ratio;\n var values = lastAutomationEvent.values.slice(0, length);\n if (fraction < 1) {\n for (var i = 1; i < length; i += 1) {\n var factor = fraction * i % 1;\n values[i] = lastAutomationEvent.values[i - 1] * (1 - factor) + lastAutomationEvent.values[i] * factor;\n }\n }\n this._automationEvents[this._automationEvents.length - 1] = createSetValueCurveAutomationEvent(values, lastAutomationEvent.startTime, duration);\n }\n }\n } else {\n var _index = this._automationEvents.findIndex(function (currentAutomationEvent) {\n return getEventTime(currentAutomationEvent) > eventTime;\n });\n var previousAutomationEvent = _index === -1 ? this._automationEvents[this._automationEvents.length - 1] : this._automationEvents[_index - 1];\n if (previousAutomationEvent !== undefined && isSetValueCurveAutomationEvent(previousAutomationEvent) && getEventTime(previousAutomationEvent) + previousAutomationEvent.duration > eventTime) {\n return false;\n }\n var persistentAutomationEvent = isExponentialRampToValueAutomationEvent(automationEvent) ? createExtendedExponentialRampToValueAutomationEvent(automationEvent.value, automationEvent.endTime, this._currenTime) : isLinearRampToValueAutomationEvent(automationEvent) ? createExtendedLinearRampToValueAutomationEvent(automationEvent.value, eventTime, this._currenTime) : automationEvent;\n if (_index === -1) {\n this._automationEvents.push(persistentAutomationEvent);\n } else {\n if (isSetValueCurveAutomationEvent(automationEvent) && eventTime + automationEvent.duration > getEventTime(this._automationEvents[_index])) {\n return false;\n }\n this._automationEvents.splice(_index, 0, persistentAutomationEvent);\n }\n }\n return true;\n }\n }, {\n key: \"flush\",\n value: function flush(time) {\n var index = this._automationEvents.findIndex(function (currentAutomationEvent) {\n return getEventTime(currentAutomationEvent) > time;\n });\n if (index > 1) {\n var remainingAutomationEvents = this._automationEvents.slice(index - 1);\n var firstRemainingAutomationEvent = remainingAutomationEvents[0];\n if (isSetTargetAutomationEvent(firstRemainingAutomationEvent)) {\n remainingAutomationEvents.unshift(createSetValueAutomationEvent(getValueOfAutomationEventAtIndexAtTime(this._automationEvents, index - 2, firstRemainingAutomationEvent.startTime, this._defaultValue), firstRemainingAutomationEvent.startTime));\n }\n this._automationEvents = remainingAutomationEvents;\n }\n }\n }, {\n key: \"getValue\",\n value: function getValue(time) {\n if (this._automationEvents.length === 0) {\n return this._defaultValue;\n }\n var indexOfNextEvent = this._automationEvents.findIndex(function (automationEvent) {\n return getEventTime(automationEvent) > time;\n });\n var nextAutomationEvent = this._automationEvents[indexOfNextEvent];\n var indexOfCurrentEvent = (indexOfNextEvent === -1 ? this._automationEvents.length : indexOfNextEvent) - 1;\n var currentAutomationEvent = this._automationEvents[indexOfCurrentEvent];\n if (currentAutomationEvent !== undefined && isSetTargetAutomationEvent(currentAutomationEvent) && (nextAutomationEvent === undefined || !isAnyRampToValueAutomationEvent(nextAutomationEvent) || nextAutomationEvent.insertTime > time)) {\n return getTargetValueAtTime(time, getValueOfAutomationEventAtIndexAtTime(this._automationEvents, indexOfCurrentEvent - 1, currentAutomationEvent.startTime, this._defaultValue), currentAutomationEvent);\n }\n if (currentAutomationEvent !== undefined && isSetValueAutomationEvent(currentAutomationEvent) && (nextAutomationEvent === undefined || !isAnyRampToValueAutomationEvent(nextAutomationEvent))) {\n return currentAutomationEvent.value;\n }\n if (currentAutomationEvent !== undefined && isSetValueCurveAutomationEvent(currentAutomationEvent) && (nextAutomationEvent === undefined || !isAnyRampToValueAutomationEvent(nextAutomationEvent) || currentAutomationEvent.startTime + currentAutomationEvent.duration > time)) {\n if (time < currentAutomationEvent.startTime + currentAutomationEvent.duration) {\n return getValueCurveValueAtTime(time, currentAutomationEvent);\n }\n return currentAutomationEvent.values[currentAutomationEvent.values.length - 1];\n }\n if (currentAutomationEvent !== undefined && isAnyRampToValueAutomationEvent(currentAutomationEvent) && (nextAutomationEvent === undefined || !isAnyRampToValueAutomationEvent(nextAutomationEvent))) {\n return currentAutomationEvent.value;\n }\n if (nextAutomationEvent !== undefined && isExponentialRampToValueAutomationEvent(nextAutomationEvent)) {\n var _getEndTimeAndValueOf = getEndTimeAndValueOfPreviousAutomationEvent(this._automationEvents, indexOfCurrentEvent, currentAutomationEvent, nextAutomationEvent, this._defaultValue),\n _getEndTimeAndValueOf2 = _slicedToArray(_getEndTimeAndValueOf, 2),\n startTime = _getEndTimeAndValueOf2[0],\n value = _getEndTimeAndValueOf2[1];\n return getExponentialRampValueAtTime(time, startTime, value, nextAutomationEvent);\n }\n if (nextAutomationEvent !== undefined && isLinearRampToValueAutomationEvent(nextAutomationEvent)) {\n var _getEndTimeAndValueOf3 = getEndTimeAndValueOfPreviousAutomationEvent(this._automationEvents, indexOfCurrentEvent, currentAutomationEvent, nextAutomationEvent, this._defaultValue),\n _getEndTimeAndValueOf4 = _slicedToArray(_getEndTimeAndValueOf3, 2),\n _startTime = _getEndTimeAndValueOf4[0],\n _value = _getEndTimeAndValueOf4[1];\n return getLinearRampValueAtTime(time, _startTime, _value, nextAutomationEvent);\n }\n return this._defaultValue;\n }\n }]);\n return AutomationEventList;\n }(Symbol.iterator);\n\n var createCancelAndHoldAutomationEvent = function createCancelAndHoldAutomationEvent(cancelTime) {\n return {\n cancelTime: cancelTime,\n type: 'cancelAndHold'\n };\n };\n\n var createCancelScheduledValuesAutomationEvent = function createCancelScheduledValuesAutomationEvent(cancelTime) {\n return {\n cancelTime: cancelTime,\n type: 'cancelScheduledValues'\n };\n };\n\n var createExponentialRampToValueAutomationEvent = function createExponentialRampToValueAutomationEvent(value, endTime) {\n return {\n endTime: endTime,\n type: 'exponentialRampToValue',\n value: value\n };\n };\n\n var createLinearRampToValueAutomationEvent = function createLinearRampToValueAutomationEvent(value, endTime) {\n return {\n endTime: endTime,\n type: 'linearRampToValue',\n value: value\n };\n };\n\n var createSetTargetAutomationEvent = function createSetTargetAutomationEvent(target, startTime, timeConstant) {\n return {\n startTime: startTime,\n target: target,\n timeConstant: timeConstant,\n type: 'setTarget'\n };\n };\n\n exports.AutomationEventList = AutomationEventList;\n exports.createCancelAndHoldAutomationEvent = createCancelAndHoldAutomationEvent;\n exports.createCancelScheduledValuesAutomationEvent = createCancelScheduledValuesAutomationEvent;\n exports.createExponentialRampToValueAutomationEvent = createExponentialRampToValueAutomationEvent;\n exports.createLinearRampToValueAutomationEvent = createLinearRampToValueAutomationEvent;\n exports.createSetTargetAutomationEvent = createSetTargetAutomationEvent;\n exports.createSetValueAutomationEvent = createSetValueAutomationEvent;\n exports.createSetValueCurveAutomationEvent = createSetValueCurveAutomationEvent;\n\n}));\n", "const {\n hasOwnProperty,\n setPrototypeOf,\n isFrozen,\n getPrototypeOf,\n getOwnPropertyDescriptor,\n} = Object;\n\nlet { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports\nlet { apply, construct } = typeof Reflect !== 'undefined' && Reflect;\n\nif (!apply) {\n apply = function (fun, thisValue, args) {\n return fun.apply(thisValue, args);\n };\n}\n\nif (!freeze) {\n freeze = function (x) {\n return x;\n };\n}\n\nif (!seal) {\n seal = function (x) {\n return x;\n };\n}\n\nif (!construct) {\n construct = function (Func, args) {\n return new Func(...args);\n };\n}\n\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayIndexOf = unapply(Array.prototype.indexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySlice = unapply(Array.prototype.slice);\n\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\n\nconst regExpTest = unapply(RegExp.prototype.test);\n\nconst typeErrorCreate = unconstruct(TypeError);\n\nexport function unapply(func) {\n return (thisArg, ...args) => apply(func, thisArg, args);\n}\n\nexport function unconstruct(func) {\n return (...args) => construct(func, args);\n}\n\n/* Add properties to a lookup table */\nexport function addToSet(set, array) {\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = stringToLowerCase(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n\n element = lcElement;\n }\n }\n\n set[element] = true;\n }\n\n return set;\n}\n\n/* Shallow clone an object */\nexport function clone(object) {\n const newObject = create(null);\n\n let property;\n for (property in object) {\n if (apply(hasOwnProperty, object, [property])) {\n newObject[property] = object[property];\n }\n }\n\n return newObject;\n}\n\n/* IE10 doesn't support __lookupGetter__ so lets'\n * simulate it. It also automatically checks\n * if the prop is function or getter and behaves\n * accordingly. */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n\n object = getPrototypeOf(object);\n }\n\n function fallbackValue(element) {\n console.warn('fallback value for', element);\n return null;\n }\n\n return fallbackValue;\n}\n\nexport {\n // Array\n arrayForEach,\n arrayIndexOf,\n arrayPop,\n arrayPush,\n arraySlice,\n // Object\n freeze,\n getPrototypeOf,\n getOwnPropertyDescriptor,\n hasOwnProperty,\n isFrozen,\n setPrototypeOf,\n seal,\n // RegExp\n regExpTest,\n // String\n stringIndexOf,\n stringMatch,\n stringReplace,\n stringToLowerCase,\n stringTrim,\n // Errors\n typeErrorCreate,\n // Other\n lookupGetter,\n};\n", "import { freeze } from './utils.js';\n\nexport const html = freeze([\n 'a',\n 'abbr',\n 'acronym',\n 'address',\n 'area',\n 'article',\n 'aside',\n 'audio',\n 'b',\n 'bdi',\n 'bdo',\n 'big',\n 'blink',\n 'blockquote',\n 'body',\n 'br',\n 'button',\n 'canvas',\n 'caption',\n 'center',\n 'cite',\n 'code',\n 'col',\n 'colgroup',\n 'content',\n 'data',\n 'datalist',\n 'dd',\n 'decorator',\n 'del',\n 'details',\n 'dfn',\n 'dialog',\n 'dir',\n 'div',\n 'dl',\n 'dt',\n 'element',\n 'em',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'font',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hgroup',\n 'hr',\n 'html',\n 'i',\n 'img',\n 'input',\n 'ins',\n 'kbd',\n 'label',\n 'legend',\n 'li',\n 'main',\n 'map',\n 'mark',\n 'marquee',\n 'menu',\n 'menuitem',\n 'meter',\n 'nav',\n 'nobr',\n 'ol',\n 'optgroup',\n 'option',\n 'output',\n 'p',\n 'picture',\n 'pre',\n 'progress',\n 'q',\n 'rp',\n 'rt',\n 'ruby',\n 's',\n 'samp',\n 'section',\n 'select',\n 'shadow',\n 'small',\n 'source',\n 'spacer',\n 'span',\n 'strike',\n 'strong',\n 'style',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'template',\n 'textarea',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'track',\n 'tt',\n 'u',\n 'ul',\n 'var',\n 'video',\n 'wbr',\n]);\n\n// SVG\nexport const svg = freeze([\n 'svg',\n 'a',\n 'altglyph',\n 'altglyphdef',\n 'altglyphitem',\n 'animatecolor',\n 'animatemotion',\n 'animatetransform',\n 'circle',\n 'clippath',\n 'defs',\n 'desc',\n 'ellipse',\n 'filter',\n 'font',\n 'g',\n 'glyph',\n 'glyphref',\n 'hkern',\n 'image',\n 'line',\n 'lineargradient',\n 'marker',\n 'mask',\n 'metadata',\n 'mpath',\n 'path',\n 'pattern',\n 'polygon',\n 'polyline',\n 'radialgradient',\n 'rect',\n 'stop',\n 'style',\n 'switch',\n 'symbol',\n 'text',\n 'textpath',\n 'title',\n 'tref',\n 'tspan',\n 'view',\n 'vkern',\n]);\n\nexport const svgFilters = freeze([\n 'feBlend',\n 'feColorMatrix',\n 'feComponentTransfer',\n 'feComposite',\n 'feConvolveMatrix',\n 'feDiffuseLighting',\n 'feDisplacementMap',\n 'feDistantLight',\n 'feFlood',\n 'feFuncA',\n 'feFuncB',\n 'feFuncG',\n 'feFuncR',\n 'feGaussianBlur',\n 'feImage',\n 'feMerge',\n 'feMergeNode',\n 'feMorphology',\n 'feOffset',\n 'fePointLight',\n 'feSpecularLighting',\n 'feSpotLight',\n 'feTile',\n 'feTurbulence',\n]);\n\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nexport const svgDisallowed = freeze([\n 'animate',\n 'color-profile',\n 'cursor',\n 'discard',\n 'fedropshadow',\n 'font-face',\n 'font-face-format',\n 'font-face-name',\n 'font-face-src',\n 'font-face-uri',\n 'foreignobject',\n 'hatch',\n 'hatchpath',\n 'mesh',\n 'meshgradient',\n 'meshpatch',\n 'meshrow',\n 'missing-glyph',\n 'script',\n 'set',\n 'solidcolor',\n 'unknown',\n 'use',\n]);\n\nexport const mathMl = freeze([\n 'math',\n 'menclose',\n 'merror',\n 'mfenced',\n 'mfrac',\n 'mglyph',\n 'mi',\n 'mlabeledtr',\n 'mmultiscripts',\n 'mn',\n 'mo',\n 'mover',\n 'mpadded',\n 'mphantom',\n 'mroot',\n 'mrow',\n 'ms',\n 'mspace',\n 'msqrt',\n 'mstyle',\n 'msub',\n 'msup',\n 'msubsup',\n 'mtable',\n 'mtd',\n 'mtext',\n 'mtr',\n 'munder',\n 'munderover',\n]);\n\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nexport const mathMlDisallowed = freeze([\n 'maction',\n 'maligngroup',\n 'malignmark',\n 'mlongdiv',\n 'mscarries',\n 'mscarry',\n 'msgroup',\n 'mstack',\n 'msline',\n 'msrow',\n 'semantics',\n 'annotation',\n 'annotation-xml',\n 'mprescripts',\n 'none',\n]);\n\nexport const text = freeze(['#text']);\n", "import { freeze } from './utils.js';\n\nexport const html = freeze([\n 'accept',\n 'action',\n 'align',\n 'alt',\n 'autocapitalize',\n 'autocomplete',\n 'autopictureinpicture',\n 'autoplay',\n 'background',\n 'bgcolor',\n 'border',\n 'capture',\n 'cellpadding',\n 'cellspacing',\n 'checked',\n 'cite',\n 'class',\n 'clear',\n 'color',\n 'cols',\n 'colspan',\n 'controls',\n 'controlslist',\n 'coords',\n 'crossorigin',\n 'datetime',\n 'decoding',\n 'default',\n 'dir',\n 'disabled',\n 'disablepictureinpicture',\n 'disableremoteplayback',\n 'download',\n 'draggable',\n 'enctype',\n 'enterkeyhint',\n 'face',\n 'for',\n 'headers',\n 'height',\n 'hidden',\n 'high',\n 'href',\n 'hreflang',\n 'id',\n 'inputmode',\n 'integrity',\n 'ismap',\n 'kind',\n 'label',\n 'lang',\n 'list',\n 'loading',\n 'loop',\n 'low',\n 'max',\n 'maxlength',\n 'media',\n 'method',\n 'min',\n 'minlength',\n 'multiple',\n 'muted',\n 'name',\n 'nonce',\n 'noshade',\n 'novalidate',\n 'nowrap',\n 'open',\n 'optimum',\n 'pattern',\n 'placeholder',\n 'playsinline',\n 'poster',\n 'preload',\n 'pubdate',\n 'radiogroup',\n 'readonly',\n 'rel',\n 'required',\n 'rev',\n 'reversed',\n 'role',\n 'rows',\n 'rowspan',\n 'spellcheck',\n 'scope',\n 'selected',\n 'shape',\n 'size',\n 'sizes',\n 'span',\n 'srclang',\n 'start',\n 'src',\n 'srcset',\n 'step',\n 'style',\n 'summary',\n 'tabindex',\n 'title',\n 'translate',\n 'type',\n 'usemap',\n 'valign',\n 'value',\n 'width',\n 'xmlns',\n 'slot',\n]);\n\nexport const svg = freeze([\n 'accent-height',\n 'accumulate',\n 'additive',\n 'alignment-baseline',\n 'ascent',\n 'attributename',\n 'attributetype',\n 'azimuth',\n 'basefrequency',\n 'baseline-shift',\n 'begin',\n 'bias',\n 'by',\n 'class',\n 'clip',\n 'clippathunits',\n 'clip-path',\n 'clip-rule',\n 'color',\n 'color-interpolation',\n 'color-interpolation-filters',\n 'color-profile',\n 'color-rendering',\n 'cx',\n 'cy',\n 'd',\n 'dx',\n 'dy',\n 'diffuseconstant',\n 'direction',\n 'display',\n 'divisor',\n 'dur',\n 'edgemode',\n 'elevation',\n 'end',\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'filter',\n 'filterunits',\n 'flood-color',\n 'flood-opacity',\n 'font-family',\n 'font-size',\n 'font-size-adjust',\n 'font-stretch',\n 'font-style',\n 'font-variant',\n 'font-weight',\n 'fx',\n 'fy',\n 'g1',\n 'g2',\n 'glyph-name',\n 'glyphref',\n 'gradientunits',\n 'gradienttransform',\n 'height',\n 'href',\n 'id',\n 'image-rendering',\n 'in',\n 'in2',\n 'k',\n 'k1',\n 'k2',\n 'k3',\n 'k4',\n 'kerning',\n 'keypoints',\n 'keysplines',\n 'keytimes',\n 'lang',\n 'lengthadjust',\n 'letter-spacing',\n 'kernelmatrix',\n 'kernelunitlength',\n 'lighting-color',\n 'local',\n 'marker-end',\n 'marker-mid',\n 'marker-start',\n 'markerheight',\n 'markerunits',\n 'markerwidth',\n 'maskcontentunits',\n 'maskunits',\n 'max',\n 'mask',\n 'media',\n 'method',\n 'mode',\n 'min',\n 'name',\n 'numoctaves',\n 'offset',\n 'operator',\n 'opacity',\n 'order',\n 'orient',\n 'orientation',\n 'origin',\n 'overflow',\n 'paint-order',\n 'path',\n 'pathlength',\n 'patterncontentunits',\n 'patterntransform',\n 'patternunits',\n 'points',\n 'preservealpha',\n 'preserveaspectratio',\n 'primitiveunits',\n 'r',\n 'rx',\n 'ry',\n 'radius',\n 'refx',\n 'refy',\n 'repeatcount',\n 'repeatdur',\n 'restart',\n 'result',\n 'rotate',\n 'scale',\n 'seed',\n 'shape-rendering',\n 'specularconstant',\n 'specularexponent',\n 'spreadmethod',\n 'startoffset',\n 'stddeviation',\n 'stitchtiles',\n 'stop-color',\n 'stop-opacity',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-opacity',\n 'stroke',\n 'stroke-width',\n 'style',\n 'surfacescale',\n 'systemlanguage',\n 'tabindex',\n 'targetx',\n 'targety',\n 'transform',\n 'transform-origin',\n 'text-anchor',\n 'text-decoration',\n 'text-rendering',\n 'textlength',\n 'type',\n 'u1',\n 'u2',\n 'unicode',\n 'values',\n 'viewbox',\n 'visibility',\n 'version',\n 'vert-adv-y',\n 'vert-origin-x',\n 'vert-origin-y',\n 'width',\n 'word-spacing',\n 'wrap',\n 'writing-mode',\n 'xchannelselector',\n 'ychannelselector',\n 'x',\n 'x1',\n 'x2',\n 'xmlns',\n 'y',\n 'y1',\n 'y2',\n 'z',\n 'zoomandpan',\n]);\n\nexport const mathMl = freeze([\n 'accent',\n 'accentunder',\n 'align',\n 'bevelled',\n 'close',\n 'columnsalign',\n 'columnlines',\n 'columnspan',\n 'denomalign',\n 'depth',\n 'dir',\n 'display',\n 'displaystyle',\n 'encoding',\n 'fence',\n 'frame',\n 'height',\n 'href',\n 'id',\n 'largeop',\n 'length',\n 'linethickness',\n 'lspace',\n 'lquote',\n 'mathbackground',\n 'mathcolor',\n 'mathsize',\n 'mathvariant',\n 'maxsize',\n 'minsize',\n 'movablelimits',\n 'notation',\n 'numalign',\n 'open',\n 'rowalign',\n 'rowlines',\n 'rowspacing',\n 'rowspan',\n 'rspace',\n 'rquote',\n 'scriptlevel',\n 'scriptminsize',\n 'scriptsizemultiplier',\n 'selection',\n 'separator',\n 'separators',\n 'stretchy',\n 'subscriptshift',\n 'supscriptshift',\n 'symmetric',\n 'voffset',\n 'width',\n 'xmlns',\n]);\n\nexport const xml = freeze([\n 'xlink:href',\n 'xml:id',\n 'xlink:title',\n 'xml:space',\n 'xmlns:xlink',\n]);\n", "import { seal } from './utils.js';\n\n// eslint-disable-next-line unicorn/better-regex\nexport const MUSTACHE_EXPR = seal(/\\{\\{[\\s\\S]*|[\\s\\S]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nexport const ERB_EXPR = seal(/<%[\\s\\S]*|[\\s\\S]*%>/gm);\nexport const DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]/); // eslint-disable-line no-useless-escape\nexport const ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nexport const IS_ALLOWED_URI = seal(\n /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nexport const IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nexport const ATTR_WHITESPACE = seal(\n /[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\n", "import * as TAGS from './tags.js';\nimport * as ATTRS from './attrs.js';\nimport * as EXPRESSIONS from './regexp.js';\nimport {\n addToSet,\n clone,\n freeze,\n arrayForEach,\n arrayPop,\n arrayPush,\n stringMatch,\n stringReplace,\n stringToLowerCase,\n stringIndexOf,\n stringTrim,\n regExpTest,\n typeErrorCreate,\n lookupGetter,\n} from './utils.js';\n\nconst getGlobal = () => (typeof window === 'undefined' ? null : window);\n\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.\n * @param {Document} document The document object (to determine policy name suffix)\n * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types\n * are not supported).\n */\nconst _createTrustedTypesPolicy = function (trustedTypes, document) {\n if (\n typeof trustedTypes !== 'object' ||\n typeof trustedTypes.createPolicy !== 'function'\n ) {\n return null;\n }\n\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (\n document.currentScript &&\n document.currentScript.hasAttribute(ATTR_NAME)\n ) {\n suffix = document.currentScript.getAttribute(ATTR_NAME);\n }\n\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn(\n 'TrustedTypes policy ' + policyName + ' could not be created.'\n );\n return null;\n }\n};\n\nfunction createDOMPurify(window = getGlobal()) {\n const DOMPurify = (root) => createDOMPurify(root);\n\n /**\n * Version label, exposed for easier checks\n * if DOMPurify is up to date or not\n */\n DOMPurify.version = VERSION;\n\n /**\n * Array of elements that DOMPurify removed during sanitation.\n * Empty if nothing was removed.\n */\n DOMPurify.removed = [];\n\n if (!window || !window.document || window.document.nodeType !== 9) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n\n return DOMPurify;\n }\n\n const originalDocument = window.document;\n\n let { document } = window;\n const {\n DocumentFragment,\n HTMLTemplateElement,\n Node,\n Element,\n NodeFilter,\n NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,\n HTMLFormElement,\n DOMParser,\n trustedTypes,\n } = window;\n\n const ElementPrototype = Element.prototype;\n\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n\n const trustedTypesPolicy = _createTrustedTypesPolicy(\n trustedTypes,\n originalDocument\n );\n const emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';\n\n const {\n implementation,\n createNodeIterator,\n createDocumentFragment,\n getElementsByTagName,\n } = document;\n const { importNode } = originalDocument;\n\n let documentMode = {};\n try {\n documentMode = clone(document).documentMode ? document.documentMode : {};\n } catch (_) {}\n\n let hooks = {};\n\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported =\n typeof getParentNode === 'function' &&\n implementation &&\n typeof implementation.createHTMLDocument !== 'undefined' &&\n documentMode !== 9;\n\n const {\n MUSTACHE_EXPR,\n ERB_EXPR,\n DATA_ATTR,\n ARIA_ATTR,\n IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE,\n } = EXPRESSIONS;\n\n let { IS_ALLOWED_URI } = EXPRESSIONS;\n\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [\n ...TAGS.html,\n ...TAGS.svg,\n ...TAGS.svgFilters,\n ...TAGS.mathMl,\n ...TAGS.text,\n ]);\n\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [\n ...ATTRS.html,\n ...ATTRS.svg,\n ...ATTRS.mathMl,\n ...ATTRS.xml,\n ]);\n\n /*\n * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n const CUSTOM_ELEMENT_HANDLING = Object.seal(\n Object.create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null,\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null,\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false,\n },\n })\n );\n\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n\n /* Output should be free from DOM clobbering attacks? */\n let SANITIZE_DOM = true;\n\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, [\n 'annotation-xml',\n 'audio',\n 'colgroup',\n 'desc',\n 'foreignobject',\n 'head',\n 'iframe',\n 'math',\n 'mi',\n 'mn',\n 'mo',\n 'ms',\n 'mtext',\n 'noembed',\n 'noframes',\n 'noscript',\n 'plaintext',\n 'script',\n 'style',\n 'svg',\n 'template',\n 'thead',\n 'title',\n 'video',\n 'xmp',\n ]);\n\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, [\n 'audio',\n 'video',\n 'img',\n 'source',\n 'image',\n 'track',\n ]);\n\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [\n 'alt',\n 'class',\n 'for',\n 'id',\n 'label',\n 'name',\n 'pattern',\n 'placeholder',\n 'role',\n 'summary',\n 'title',\n 'value',\n 'style',\n 'xmlns',\n ]);\n\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc;\n\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n\n const formElement = document.createElement('form');\n\n const isRegexOrFunction = function (testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n\n /**\n * _parseConfig\n *\n * @param {Object} cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function (cfg) {\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n\n /* Set configuration parameters */\n ALLOWED_TAGS =\n 'ALLOWED_TAGS' in cfg\n ? addToSet({}, cfg.ALLOWED_TAGS)\n : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR =\n 'ALLOWED_ATTR' in cfg\n ? addToSet({}, cfg.ALLOWED_ATTR)\n : DEFAULT_ALLOWED_ATTR;\n URI_SAFE_ATTRIBUTES =\n 'ADD_URI_SAFE_ATTR' in cfg\n ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR)\n : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS =\n 'ADD_DATA_URI_TAGS' in cfg\n ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS)\n : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS =\n 'FORBID_CONTENTS' in cfg\n ? addToSet({}, cfg.FORBID_CONTENTS)\n : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};\n FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};\n USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n if (\n cfg.CUSTOM_ELEMENT_HANDLING &&\n isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)\n ) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck =\n cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n }\n\n if (\n cfg.CUSTOM_ELEMENT_HANDLING &&\n isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)\n ) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck =\n cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n }\n\n if (\n cfg.CUSTOM_ELEMENT_HANDLING &&\n typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements ===\n 'boolean'\n ) {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements =\n cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n }\n\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1\n ? (PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE)\n : (PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE);\n\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc =\n PARSER_MEDIA_TYPE === 'application/xhtml+xml'\n ? (x) => x\n : stringToLowerCase;\n\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, [...TAGS.text]);\n ALLOWED_ATTR = [];\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, TAGS.html);\n addToSet(ALLOWED_ATTR, ATTRS.html);\n }\n\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, TAGS.svg);\n addToSet(ALLOWED_ATTR, ATTRS.svg);\n addToSet(ALLOWED_ATTR, ATTRS.xml);\n }\n\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, TAGS.svgFilters);\n addToSet(ALLOWED_ATTR, ATTRS.svg);\n addToSet(ALLOWED_ATTR, ATTRS.xml);\n }\n\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, TAGS.mathMl);\n addToSet(ALLOWED_ATTR, ATTRS.mathMl);\n addToSet(ALLOWED_ATTR, ATTRS.xml);\n }\n }\n\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);\n }\n\n if (cfg.ADD_ATTR) {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);\n }\n\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);\n }\n\n if (cfg.FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);\n }\n\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n\n CONFIG = cfg;\n };\n\n const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [\n 'mi',\n 'mo',\n 'mn',\n 'ms',\n 'mtext',\n ]);\n\n const HTML_INTEGRATION_POINTS = addToSet({}, [\n 'foreignobject',\n 'desc',\n 'title',\n 'annotation-xml',\n ]);\n\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, TAGS.svg);\n addToSet(ALL_SVG_TAGS, TAGS.svgFilters);\n addToSet(ALL_SVG_TAGS, TAGS.svgDisallowed);\n\n const ALL_MATHML_TAGS = addToSet({}, TAGS.mathMl);\n addToSet(ALL_MATHML_TAGS, TAGS.mathMlDisallowed);\n\n /**\n *\n *\n * @param {Element} element a DOM element whose namespace is being checked\n * @returns {boolean} Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function (element) {\n let parent = getParentNode(element);\n\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: HTML_NAMESPACE,\n tagName: 'template',\n };\n }\n\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n\n // The only way to switch from MathML to SVG is via\n // svg if parent is either or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return (\n tagName === 'svg' &&\n (parentTagName === 'annotation-xml' ||\n MATHML_TEXT_INTEGRATION_POINTS[parentTagName])\n );\n }\n\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n\n // The only way to switch from SVG to MathML is via\n // and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (\n parent.namespaceURI === SVG_NAMESPACE &&\n !HTML_INTEGRATION_POINTS[parentTagName]\n ) {\n return false;\n }\n\n if (\n parent.namespaceURI === MATHML_NAMESPACE &&\n !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]\n ) {\n return false;\n }\n\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erronously deleted from\n // HTML namespace.\n const commonSvgAndHTMLElements = addToSet({}, [\n 'title',\n 'style',\n 'font',\n 'a',\n 'script',\n ]);\n\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return (\n !ALL_MATHML_TAGS[tagName] &&\n (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName])\n );\n }\n\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG or MathML). Return false just in case.\n return false;\n };\n\n /**\n * _forceRemove\n *\n * @param {Node} node a DOM node\n */\n const _forceRemove = function (node) {\n arrayPush(DOMPurify.removed, { element: node });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n node.parentNode.removeChild(node);\n } catch (_) {\n try {\n node.outerHTML = emptyHTML;\n } catch (_) {\n node.remove();\n }\n }\n };\n\n /**\n * _removeAttribute\n *\n * @param {String} name an Attribute name\n * @param {Node} node a DOM node\n */\n const _removeAttribute = function (name, node) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: node.getAttributeNode(name),\n from: node,\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: node,\n });\n }\n\n node.removeAttribute(name);\n\n // We void attribute values for unremovable \"is\"\" attributes\n if (name === 'is' && !ALLOWED_ATTR[name]) {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(node);\n } catch (_) {}\n } else {\n try {\n node.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n\n /**\n * _initDocument\n *\n * @param {String} dirty a string of dirty markup\n * @return {Document} a DOM, filled with the dirty markup\n */\n const _initDocument = function (dirty) {\n /* Create a HTML document */\n let doc;\n let leadingWhitespace;\n\n if (FORCE_BODY) {\n dirty = ' ' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n const matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {\n // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)\n dirty =\n '' +\n dirty +\n '';\n }\n\n const dirtyPayload = trustedTypesPolicy\n ? trustedTypesPolicy.createHTML(dirty)\n : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n } catch (_) {}\n }\n\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n\n const body = doc.body || doc.documentElement;\n\n if (dirty && leadingWhitespace) {\n body.insertBefore(\n document.createTextNode(leadingWhitespace),\n body.childNodes[0] || null\n );\n }\n\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(\n doc,\n WHOLE_DOCUMENT ? 'html' : 'body'\n )[0];\n }\n\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n\n /**\n * _createIterator\n *\n * @param {Document} root document/fragment to create iterator for\n * @return {Iterator} iterator instance\n */\n const _createIterator = function (root) {\n return createNodeIterator.call(\n root.ownerDocument || root,\n root,\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT,\n null,\n false\n );\n };\n\n /**\n * _isClobbered\n *\n * @param {Node} elm element to check for clobbering attacks\n * @return {Boolean} true if clobbered, false if safe\n */\n const _isClobbered = function (elm) {\n return (\n elm instanceof HTMLFormElement &&\n (typeof elm.nodeName !== 'string' ||\n typeof elm.textContent !== 'string' ||\n typeof elm.removeChild !== 'function' ||\n !(elm.attributes instanceof NamedNodeMap) ||\n typeof elm.removeAttribute !== 'function' ||\n typeof elm.setAttribute !== 'function' ||\n typeof elm.namespaceURI !== 'string' ||\n typeof elm.insertBefore !== 'function')\n );\n };\n\n /**\n * _isNode\n *\n * @param {Node} obj object to check whether it's a DOM node\n * @return {Boolean} true is object is a DOM node\n */\n const _isNode = function (object) {\n return typeof Node === 'object'\n ? object instanceof Node\n : object &&\n typeof object === 'object' &&\n typeof object.nodeType === 'number' &&\n typeof object.nodeName === 'string';\n };\n\n /**\n * _executeHook\n * Execute user configurable hooks\n *\n * @param {String} entryPoint Name of the hook's entry point\n * @param {Node} currentNode node to work on with the hook\n * @param {Object} data additional hook parameters\n */\n const _executeHook = function (entryPoint, currentNode, data) {\n if (!hooks[entryPoint]) {\n return;\n }\n\n arrayForEach(hooks[entryPoint], (hook) => {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n };\n\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n *\n * @param {Node} currentNode to check for permission to exist\n * @return {Boolean} true if node was killed, false if left alive\n */\n const _sanitizeElements = function (currentNode) {\n let content;\n\n /* Execute a hook if present */\n _executeHook('beforeSanitizeElements', currentNode, null);\n\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Check if tagname contains Unicode */\n if (stringMatch(currentNode.nodeName, /[\\u0080-\\uFFFF]/)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Now let's check the element's type and name */\n const tagName = transformCaseFunc(currentNode.nodeName);\n\n /* Execute a hook if present */\n _executeHook('uponSanitizeElement', currentNode, {\n tagName,\n allowedTags: ALLOWED_TAGS,\n });\n\n /* Detect mXSS attempts abusing namespace confusion */\n if (\n !_isNode(currentNode.firstElementChild) &&\n (!_isNode(currentNode.content) ||\n !_isNode(currentNode.content.firstElementChild)) &&\n regExpTest(/<[/\\w]/g, currentNode.innerHTML) &&\n regExpTest(/<[/\\w]/g, currentNode.textContent)\n ) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Mitigate a problem with templates inside select */\n if (\n tagName === 'select' &&\n regExpTest(/= 0; --i) {\n parentNode.insertBefore(\n cloneNode(childNodes[i], true),\n getNextSibling(currentNode)\n );\n }\n }\n }\n\n if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&\n regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)\n )\n return false;\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&\n CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)\n )\n return false;\n }\n\n _forceRemove(currentNode);\n return true;\n }\n\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n\n if (\n (tagName === 'noscript' || tagName === 'noembed') &&\n regExpTest(/<\\/no(script|embed)/i, currentNode.innerHTML)\n ) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {\n /* Get the element's text content */\n content = currentNode.textContent;\n content = stringReplace(content, MUSTACHE_EXPR, ' ');\n content = stringReplace(content, ERB_EXPR, ' ');\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });\n currentNode.textContent = content;\n }\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeElements', currentNode, null);\n\n return false;\n };\n\n /**\n * _isValidAttribute\n *\n * @param {string} lcTag Lowercase tag name of containing element.\n * @param {string} lcName Lowercase attribute name.\n * @param {string} value Attribute value.\n * @return {Boolean} Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n const _isValidAttribute = function (lcTag, lcName, value) {\n /* Make sure attribute cannot clobber */\n if (\n SANITIZE_DOM &&\n (lcName === 'id' || lcName === 'name') &&\n (value in document || value in formElement)\n ) {\n return false;\n }\n\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (\n ALLOW_DATA_ATTR &&\n !FORBID_ATTR[lcName] &&\n regExpTest(DATA_ATTR, lcName)\n ) {\n // This attribute is safe\n } else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) {\n // This attribute is safe\n /* Otherwise, check the name is permitted */\n } else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n if (\n // First condition does a very basic check if a) it's basically a valid custom element tagname AND\n // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck\n (_basicCustomElementTest(lcTag) &&\n ((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&\n regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag)) ||\n (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&\n CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag))) &&\n ((CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp &&\n regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName)) ||\n (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function &&\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)))) ||\n // Alternative, second condition checks if it's an `is`-attribute, AND\n // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n (lcName === 'is' &&\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements &&\n ((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&\n regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value)) ||\n (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&\n CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))))\n ) {\n // If user has supplied a regexp or function in CUSTOM_ELEMENT_HANDLING.tagNameCheck, we need to also allow derived custom elements using the same tagName test.\n // Additionally, we need to allow attributes passing the CUSTOM_ELEMENT_HANDLING.attributeNameCheck user has configured, as custom elements can define these at their own discretion.\n } else {\n return false;\n }\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) {\n // This attribute is safe\n /* Check no script, data or unknown possibly unsafe URI\n unless we know URI values are safe for that attribute */\n } else if (\n regExpTest(IS_ALLOWED_URI, stringReplace(value, ATTR_WHITESPACE, ''))\n ) {\n // This attribute is safe\n /* Keep image data URIs alive if src/xlink:href is allowed */\n /* Further prevent gadget XSS for dynamically built script tags */\n } else if (\n (lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') &&\n lcTag !== 'script' &&\n stringIndexOf(value, 'data:') === 0 &&\n DATA_URI_TAGS[lcTag]\n ) {\n // This attribute is safe\n /* Allow unknown protocols: This provides support for links that\n are handled by protocol handlers which may be unknown ahead of\n time, e.g. fb:, spotify: */\n } else if (\n ALLOW_UNKNOWN_PROTOCOLS &&\n !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))\n ) {\n // This attribute is safe\n /* Check for binary attributes */\n // eslint-disable-next-line no-negated-condition\n } else if (!value) {\n // Binary attributes are safe at this point\n /* Anything else, presume unsafe, do not add it back */\n } else {\n return false;\n }\n\n return true;\n };\n\n /**\n * _basicCustomElementCheck\n * checks if at least one dash is included in tagName, and it's not the first char\n * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name\n * @param {string} tagName name of the tag of the node to sanitize\n */\n const _basicCustomElementTest = function (tagName) {\n return tagName.indexOf('-') > 0;\n };\n\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param {Node} currentNode to sanitize\n */\n const _sanitizeAttributes = function (currentNode) {\n let attr;\n let value;\n let lcName;\n let l;\n /* Execute a hook if present */\n _executeHook('beforeSanitizeAttributes', currentNode, null);\n\n const { attributes } = currentNode;\n\n /* Check if we have attributes; if not we might have a text node */\n if (!attributes) {\n return;\n }\n\n const hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR,\n };\n l = attributes.length;\n\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n attr = attributes[l];\n const { name, namespaceURI } = attr;\n value = stringTrim(attr.value);\n lcName = transformCaseFunc(name);\n\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHook('uponSanitizeAttribute', currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n\n /* Remove attribute */\n _removeAttribute(name, currentNode);\n\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n continue;\n }\n\n /* Work around a security issue in jQuery 3.0 */\n if (regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n value = stringReplace(value, MUSTACHE_EXPR, ' ');\n value = stringReplace(value, ERB_EXPR, ' ');\n }\n\n /* Is `value` valid for this attribute? */\n const lcTag = transformCaseFunc(currentNode.nodeName);\n if (!_isValidAttribute(lcTag, lcName, value)) {\n continue;\n }\n\n /* Handle invalid data-* attribute set by try-catching it */\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n\n arrayPop(DOMPurify.removed);\n } catch (_) {}\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeAttributes', currentNode, null);\n };\n\n /**\n * _sanitizeShadowDOM\n *\n * @param {DocumentFragment} fragment to iterate over recursively\n */\n const _sanitizeShadowDOM = function (fragment) {\n let shadowNode;\n const shadowIterator = _createIterator(fragment);\n\n /* Execute a hook if present */\n _executeHook('beforeSanitizeShadowDOM', fragment, null);\n\n while ((shadowNode = shadowIterator.nextNode())) {\n /* Execute a hook if present */\n _executeHook('uponSanitizeShadowNode', shadowNode, null);\n\n /* Sanitize tags and elements */\n if (_sanitizeElements(shadowNode)) {\n continue;\n }\n\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n\n /* Check attributes, sanitize if necessary */\n _sanitizeAttributes(shadowNode);\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeShadowDOM', fragment, null);\n };\n\n /**\n * Sanitize\n * Public method providing core sanitation functionality\n *\n * @param {String|Node} dirty string or DOM node\n * @param {Object} configuration object\n */\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty, cfg) {\n let body;\n let importedNode;\n let currentNode;\n let oldNode;\n let returnNode;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '';\n }\n\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n // eslint-disable-next-line no-negated-condition\n if (typeof dirty.toString !== 'function') {\n throw typeErrorCreate('toString is not a function');\n } else {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n }\n }\n\n /* Check we can run. Otherwise fall back or ignore */\n if (!DOMPurify.isSupported) {\n if (\n typeof window.toStaticHTML === 'object' ||\n typeof window.toStaticHTML === 'function'\n ) {\n if (typeof dirty === 'string') {\n return window.toStaticHTML(dirty);\n }\n\n if (_isNode(dirty)) {\n return window.toStaticHTML(dirty.outerHTML);\n }\n }\n\n return dirty;\n }\n\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n\n /* Clean up removed elements */\n DOMPurify.removed = [];\n\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n\n if (IN_PLACE) {\n /* Do some early pre-sanitization to avoid unsafe root nodes */\n if (dirty.nodeName) {\n const tagName = transformCaseFunc(dirty.nodeName);\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n throw typeErrorCreate(\n 'root node is forbidden and cannot be sanitized in-place'\n );\n }\n }\n } else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (\n !RETURN_DOM &&\n !SAFE_FOR_TEMPLATES &&\n !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1\n ) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE\n ? trustedTypesPolicy.createHTML(dirty)\n : dirty;\n }\n\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n }\n }\n\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n\n /* Get node iterator */\n const nodeIterator = _createIterator(IN_PLACE ? dirty : body);\n\n /* Now start iterating over the created document */\n while ((currentNode = nodeIterator.nextNode())) {\n /* Fix IE's strange behavior with manipulated textNodes #89 */\n if (currentNode.nodeType === 3 && currentNode === oldNode) {\n continue;\n }\n\n /* Sanitize tags and elements */\n if (_sanitizeElements(currentNode)) {\n continue;\n }\n\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n\n /* Check attributes, sanitize if necessary */\n _sanitizeAttributes(currentNode);\n\n oldNode = currentNode;\n }\n\n oldNode = null;\n\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n\n if (ALLOWED_ATTR.shadowroot) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n\n return returnNode;\n }\n\n let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');\n serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');\n }\n\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE\n ? trustedTypesPolicy.createHTML(serializedHTML)\n : serializedHTML;\n };\n\n /**\n * Public method to set the configuration once\n * setConfig\n *\n * @param {Object} cfg configuration object\n */\n DOMPurify.setConfig = function (cfg) {\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n\n /**\n * Public method to remove the configuration\n * clearConfig\n *\n */\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n\n /**\n * Public method to check if an attribute value is valid.\n * Uses last set config, if any. Otherwise, uses config defaults.\n * isValidAttribute\n *\n * @param {string} tag Tag name of containing element.\n * @param {string} attr Attribute name.\n * @param {string} value Attribute value.\n * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.\n */\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n\n const lcTag = transformCaseFunc(tag);\n const lcName = transformCaseFunc(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n\n /**\n * AddHook\n * Public method to add DOMPurify hooks\n *\n * @param {String} entryPoint entry point for the hook to add\n * @param {Function} hookFunction function to execute\n */\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n\n hooks[entryPoint] = hooks[entryPoint] || [];\n arrayPush(hooks[entryPoint], hookFunction);\n };\n\n /**\n * RemoveHook\n * Public method to remove a DOMPurify hook at a given entryPoint\n * (pops it from the stack of hooks if more are present)\n *\n * @param {String} entryPoint entry point for the hook to remove\n */\n DOMPurify.removeHook = function (entryPoint) {\n if (hooks[entryPoint]) {\n arrayPop(hooks[entryPoint]);\n }\n };\n\n /**\n * RemoveHooks\n * Public method to remove all DOMPurify hooks at a given entryPoint\n *\n * @param {String} entryPoint entry point for the hooks to remove\n */\n DOMPurify.removeHooks = function (entryPoint) {\n if (hooks[entryPoint]) {\n hooks[entryPoint] = [];\n }\n };\n\n /**\n * RemoveAllHooks\n * Public method to remove all DOMPurify hooks\n *\n */\n DOMPurify.removeAllHooks = function () {\n hooks = {};\n };\n\n return DOMPurify;\n}\n\nexport default createDOMPurify();\n", "/**\n * Entry file for the JavaScript webapp\n *\n * Functions declared as 'export function' inside modules that are \n * exported here will be available in the HTML as `hedyApp.myFunction(...)`.\n *\n * Files that don't export any functions that are used directly from the HTML\n * should be included by using an 'import' statement.\n *\n * If you want to do any work upon loading the page (such as attaching DOM event\n * listeners), define and export an initialization function, and call that \n * from 'initialize.ts'.\n */\nimport './htmx-integration';\nexport * from './modal';\nexport * from './app';\nexport * from './auth';\nexport * from './statistics';\nexport * from './logs';\nexport * from './tutorials/tutorial';\nexport * from './teachers';\nexport * from './browser-helpers/unsaved-changes';\nexport * from './initialize';\nexport * from './debugging';\nexport { getPreviousAndNext } from './tabs';\nexport * from './tailwind';\nexport * from './public-adventures';\nexport { loadParsonsExercise } from './parsons';\n", "import REGEX from '../../highlighting/highlighting.json';\nimport TRADUCTION_IMPORT from '../../highlighting/highlighting-trad.json';\n\nexport interface InitializeSyntaxHighlighterOptions {\n readonly keywordLanguage: string;\n}\n\n/**\n * Initialize syntax highlighters\n *\n * Must be done before the first Ace editor is created\n */\nexport function initializeSyntaxHighlighter(options: InitializeSyntaxHighlighterOptions) {\n // import traduction\n let TRADUCTIONS = convert(TRADUCTION_IMPORT) as Map>;\n\n let lang = options.keywordLanguage;\n\n if (!TRADUCTIONS.has(lang)) { lang = 'en'; }\n\n // get the traduction\n var TRADUCTION = TRADUCTIONS.get(lang) as Map ;\n\n\n // translate regex\n var data = JSON.stringify(REGEX);\n var data_tr = convertReg(data,TRADUCTION);\n var LEVELS = JSON.parse(data_tr);\n\n\n // Only do this work if the 'define' function is actually available at runtime.\n // If not, this script got included on a page that didn't include the Ace\n // editor. No point in continuing if that is the case.\n if ((window as any).define) {\n // Define the modes based on the level definitions above\n for (const level of LEVELS) {\n // This is a local definition of the file 'ace/mode/level1.js', etc.\n define('ace/mode/' + level.name, [], function(require, exports, _module) {\n var oop = require('ace/lib/oop');\n var TextMode = require('ace/mode/text').Mode;\n var TextHighlightRules = require('ace/mode/text_highlight_rules').TextHighlightRules;\n\n function ThisLevelHighlightRules(this: any) {\n this.$rules = level.rules;\n this.normalizeRules();\n };\n oop.inherits(ThisLevelHighlightRules, TextHighlightRules);\n\n function Mode(this: any) {\n this.HighlightRules = ThisLevelHighlightRules;\n };\n oop.inherits(Mode, TextMode);\n\n exports.Mode = Mode;\n });\n }\n }\n}\n\n// convert an objet in a map\nexport function convert(o:(object|undefined)) {\n if (typeof o === 'object') {\n let tmp:Map = new Map(Object.entries(o));\n\n let ret:Map = new Map();\n\n tmp.forEach((value, key) => {\n ret.set(key, convert(value));\n });\n\n return ret;\n } else {\n return o;\n }\n}\n\n// here we need to transfome ____ in a current kayword with translation\nexport function convertReg(oldReg:string, TRAD:Map ) {\n var newReg = oldReg;\n\n TRAD.forEach((value,key) => {\n key = key;\n var reg = new RegExp('__' + key + '__','g');\n newReg = newReg.replace(reg, value);\n });\n\n return newReg;\n}\n\n\n\n", "// This file has been generated by build-tools/heroku/generate-client-messages.py\n// DO NOT EDIT\n\nexport type MessageKey = 'CheckInternet' | 'Connection_error' | 'Empty_output' | 'Errors_found' | 'Execute_error' | 'Other_error' | 'Program_repair' | 'Program_too_long' | 'ServerError' | 'Transpile_error' | 'Transpile_success' | 'Transpile_warning' | 'Unsaved_Changes' | 'adventures_restored' | 'copy_link_to_share' | 'customization_deleted' | 'dice' | 'directly_available' | 'disabled' | 'fortune' | 'haunted' | 'level_title' | 'restaurant' | 'rock' | 'songs' | 'teacher_welcome' | 'turtle' | 'unsaved_class_changes'\nexport const TRANSLATIONS: Record> = {\n \"ar\": {\n \"CheckInternet\": \"\u0623\u0644\u0642\u064A \u0646\u0638\u0631\u0629 \u0625\u0630\u0627 \u0643\u0627\u0646 \u0627\u062A\u0635\u0627\u0644\u0643 \u0628\u0627\u0644\u0625\u0646\u062A\u0631\u0646\u062A \u064A\u0639\u0645\u0644 \u0628\u0634\u0643\u0644 \u0635\u062D\u064A\u062D.\",\n \"Connection_error\": \"\u0644\u0645 \u0646\u062A\u0645\u0643\u0646 \u0645\u0646 \u0627\u0644\u0648\u0635\u0648\u0644 \u0625\u0644\u0649 \u0627\u0644\u062E\u0627\u062F\u0645.\",\n \"Empty_output\": \"\u0647\u0630\u0627 \u0627\u0644\u0631\u0645\u0632 \u064A\u0639\u0645\u0644 \u0648\u0644\u0643\u0646 \u0644\u0627 \u064A\u0637\u0628\u0639 \u0623\u064A \u0634\u064A\u0621. \u0623\u0636\u0641 \u0623\u0645\u0631 \u0637\u0628\u0627\u0639\u0629 \u0625\u0644\u0649 \u0627\u0644\u0643\u0648\u062F \u0627\u0644\u062E\u0627\u0635 \u0628\u0643 \u0623\u0648 \u0627\u0633\u062A\u062E\u062F\u0645 \u0627\u0644\u0633\u0644\u062D\u0641\u0627\u0629 \u0644\u0637\u0628\u0627\u0639\u0629 \u0634\u064A\u0621 \u0645\u0627.\",\n \"Errors_found\": \"\u0644\u0642\u062F \u0642\u0645\u062A \u0628\u062E\u0637\u0623! \u0644\u0627 \u062A\u0642\u0644\u0642\u060C \u0644\u0642\u062F \u0642\u0645\u0646\u0627 \u0628\u062A\u0634\u063A\u064A\u0644 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C\",\n \"Execute_error\": \"\u062D\u062F\u062B \u062E\u0637\u0623 \u0645\u0627 \u0623\u062B\u0646\u0627\u0621 \u062A\u0634\u063A\u064A\u0644 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C.\",\n \"Other_error\": \"\u0639\u0641\u0648\u0627! \u0631\u0628\u0645\u0627 \u0627\u0631\u062A\u0643\u0628\u0646\u0627 \u062E\u0637\u0623 \u0628\u0633\u064A\u0637\u0627.\",\n \"Program_repair\": \"\u0642\u062F \u064A\u0643\u0648\u0646 \u0647\u0630\u0627 \u0647\u0648 \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0635\u062D\u064A\u062D \u060C \u0647\u0644 \u064A\u0645\u0643\u0646\u0643 \u0625\u0635\u0644\u0627\u062D\u0647\u061F\",\n \"Program_too_long\": \"\u064A\u0633\u062A\u063A\u0631\u0642 \u0628\u0631\u0646\u0627\u0645\u062C\u0643 \u0648\u0642\u062A\u064B\u0627 \u0637\u0648\u064A\u0644\u0627\u064B \u0644\u0644\u062A\u0634\u063A\u064A\u0644.\",\n \"ServerError\": \"\u0644\u0642\u062F \u0643\u062A\u0628\u062A \u0628\u0631\u0646\u0627\u0645\u062C\u0627 \u0644\u0645 \u0646\u0643\u0646 \u0646\u062A\u0648\u0642\u0639\u0647. \u0625\u0630\u0627 \u0643\u0646\u062A \u062A\u0631\u063A\u0628 \u0641\u064A \u0627\u0644\u0645\u0633\u0627\u0639\u062F\u0629 \u060C \u0641\u0623\u0631\u0633\u0644 \u0644\u0646\u0627 \u0628\u0631\u064A\u062F\u0627 \u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A\u0627 \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0627\u0644\u0645\u0633\u062A\u0648\u0649 \u0648\u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062E\u0627\u0635 \u0628\u0643 \u0639\u0644\u0649 hello@hedy.org. \u0641\u064A \u063A\u0636\u0648\u0646 \u0630\u0644\u0643 \u060C \u062C\u0631\u0628 \u0634\u064A\u0626\u0627 \u0645\u062E\u062A\u0644\u0641\u0627 \u0642\u0644\u064A\u0644\u0627 \u0648\u0623\u0644\u0642 \u0646\u0638\u0631\u0629 \u0623\u062E\u0631\u0649 \u0639\u0644\u0649 \u0627\u0644\u0623\u0645\u062B\u0644\u0629. \u0634\u0643\u0631\u0627!\",\n \"Transpile_error\": \"\u0644\u0627 \u064A\u0645\u0643\u0646\u0646\u0627 \u062A\u0634\u063A\u064A\u0644 \u0628\u0631\u0646\u0627\u0645\u062C\u0643.\",\n \"Transpile_success\": \"\u0623\u062D\u0633\u0646\u062A!\\n\u0631\u0627\u0626\u0639!\\n\u0623\u062D\u0633\u0646\u062A!\\n\u0645\u0645\u062A\u0627\u0632!\\n\u0644\u0642\u062F \u0642\u0645\u062A \u0628\u0639\u0645\u0644 \u0631\u0627\u0626\u0639!\",\n \"Transpile_warning\": \"\u062A\u062D\u0630\u064A\u0631!\",\n \"Unsaved_Changes\": \"\u0644\u062F\u064A\u0643 \u0628\u0631\u0646\u0627\u0645\u062C \u063A\u064A\u0631 \u0645\u062D\u0641\u0648\u0638. \u0647\u0644 \u062A\u0631\u064A\u062F \u0627\u0644\u0645\u063A\u0627\u062F\u0631\u0629 \u062F\u0648\u0646 \u062D\u0641\u0638\u0647\u061F\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"\u0627\u0646\u0633\u062E \u0627\u0644\u0631\u0627\u0628\u0637 \u0644\u0645\u0634\u0627\u0631\u0643\u062A\u0647\",\n \"customization_deleted\": \"\u062A\u0645 \u062D\u0630\u0641 \u0627\u0644\u062A\u062E\u0635\u064A\u0635\u0627\u062A \u0628\u0646\u062C\u0627\u062D.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u0627\u0641\u062A\u062A\u062D \u0645\u0628\u0627\u0634\u0631\u0629\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u0645\u0633\u062A\u0648\u0649\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"\u0645\u0631\u062D\u0628\u0627\u064B \u0641\u064A \u0647\u064A\u062F\u064A! \u0628\u0643\u0644 \u0641\u062E\u0631 \u0623\u0646\u062A \u0627\u0644\u0622\u0646 \u062A\u0645\u062A\u0644\u0643 \u062D\u0633\u0627\u0628\u0627\u064B \u0643\u0645\u0639\u0644\u0645 \u0648\u0630\u0627 \u064A\u0633\u0645\u062D \u0644\u0643 \u0628\u0627\u0646\u0634\u0627\u0621 \u0635\u0641\u0648\u0641 \u0648\u062F\u0639\u0648\u0629 \u062A\u0644\u0627\u0645\u064A\u0630.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"bg\": {\n \"CheckInternet\": \"\u041F\u0440\u043E\u0431\u043B\u0435\u043C\u0438 \u0441 \u0438\u043D\u0442\u0435\u0440\u043D\u0435\u0442\u043D\u0430\u0442\u0430 \u0442\u0438 \u0432\u0440\u044A\u0437\u043A\u0430. \u0429\u0435 \u044F \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0448 \u043B\u0438.\",\n \"Connection_error\": \"\u0418\u0437\u0433\u0443\u0431\u0438\u0445\u043C\u0435 \u0432\u0440\u044A\u0437\u043A\u0430 \u0441\u044A\u0441 \u0441\u044A\u0440\u0432\u044A\u0440\u0430.\",\n \"Empty_output\": \"\u041A\u043E\u0434\u044A\u0442 \u0440\u0430\u0431\u043E\u0442\u0438, \u043D\u043E \u043D\u044F\u043C\u0430 \u043D\u0438\u0449\u043E \u0437\u0430 \u043F\u0440\u0438\u043D\u0442\u0438\u0440\u0430\u043D\u0435.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u041D\u0435\u0449\u043E \u0441\u0435 \u043E\u0431\u044A\u0440\u043A\u0430 \u043F\u0440\u0438 \u0438\u0437\u043F\u044A\u043B\u043D\u0435\u043D\u0438\u0435\u0442\u043E \u043D\u0430 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0430\u0442\u0430.\",\n \"Other_error\": \"\u041E\u0439! \u041C\u0430\u0439 \u0438\u043C\u0430\u043C\u0435 \u043D\u044F\u043A\u0430\u043A\u0432\u0430 \u0433\u0440\u0435\u0448\u043A\u0430 \u0432 \u043A\u043E\u0434\u0430.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"\u041F\u0442\u043E\u0433\u0440\u0430\u043C\u0430\u0442\u0430 \u0442\u0438 \u0441\u0435 \u0438\u0437\u043F\u044A\u043B\u043D\u044F\u0432\u0430 \u043F\u0440\u0435\u043A\u0430\u043B\u0435\u043D\u043E \u0434\u044A\u043B\u0433\u043E \u0432\u0440\u0435\u043C\u0435...\",\n \"ServerError\": \"\u041D\u0430\u043F\u0438\u0441\u0430\u043B(\u0430) \u0441\u0438 \u043A\u043E\u0434, \u043A\u043E\u0439\u0442\u043E \u043D\u0435 \u043E\u0447\u0430\u043A\u0432\u0430\u0445\u043C\u0435. \u041F\u043E\u043C\u043E\u0433\u043D\u0438 \u043D\u0438 \u0438 \u043D\u0438 \u0438\u0437\u043F\u0440\u0430\u0442\u0438 \u0438\u043C\u0435\u0439\u043B \u043D\u0430 hello@hedy.org \u0441 \u0438\u043C\u0435\u0442\u043E \u043D\u0430 \u043D\u0438\u0432\u043E\u0442\u043E, \u0432 \u043A\u043E\u0435\u0442\u043E \u0437\u0430\u0441\u0435\u0434\u043D\u0430. \u041F\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043B\u043D\u043E \u0431\u043B\u0430\u0433\u043E\u0434\u0430\u0440\u0438\u043C!\",\n \"Transpile_error\": \"\u041F\u0440\u043E\u0433\u0440\u0430\u043C\u0430\u0442\u0430 \u0442\u0438 \u043D\u0435 \u043C\u043E\u0436\u0435 \u0434\u0430 \u0431\u044A\u0434\u0435 \u0438\u0437\u043F\u044A\u043B\u043D\u0435\u043D\u0430.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"\u0412\u043D\u0438\u043C\u0430\u043D\u0438\u0435!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u041D\u0438\u0432\u043E\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"bn\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ca\": {\n \"CheckInternet\": \"Mireu si la vostra connexi\u00F3 a Internet funciona correctament.\",\n \"Connection_error\": \"No hem pogut arribar al servidor.\",\n \"Empty_output\": \"Aquest codi funciona, per\u00F2 no imprimeix res. Afegiu una comanda d'impressi\u00F3 al vostre codi o utilitzeu la tortuga per obtenir la sortida.\",\n \"Errors_found\": \"Has com\u00E8s un error! No et preocupis, encara podem executar el programa\",\n \"Execute_error\": \"S'ha produ\u00EFt un error en executar el programa.\",\n \"Other_error\": \"Vaja! Potser ens hem equivocat una mica.\",\n \"Program_repair\": \"Aquest podria ser el codi correcte, el pots arreglar?\",\n \"Program_too_long\": \"El vostre programa triga massa a executar-se.\",\n \"ServerError\": \"Has escrit un programa que no esper\u00E0vem. Si vols ajudar, envia'ns un correu amb el nivell i el teu programa a hello@hedy.org. Mentrestant, proveu alguna cosa una mica diferent i feu una altra ullada als exemples. Gr\u00E0cies!\",\n \"Transpile_error\": \"No podem executar el vostre programa.\",\n \"Transpile_success\": \"Ben fet!\\nIncre\u00EFble!\\nBen fet!\\nExcel\u00B7lent!\\nHo has fet genial!\",\n \"Transpile_warning\": \"Vigila!\",\n \"Unsaved_Changes\": \"Tens un programa sense desar. Vols marxar sense desar-lo?\",\n \"adventures_restored\": \"Les aventures per defecte han estat restaurades.\",\n \"copy_link_to_share\": \"Copia l'enlla\u00E7 per compartir\",\n \"customization_deleted\": \"Personalitzacions esborrades correctament.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Obrir directament\",\n \"disabled\": \"Desactivat\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Nivell\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5, \uD83C\uDFB6\",\n \"teacher_welcome\": \"Benvinguda a Hedy! El teu compte s'ha actualitzat a compte de professorat. Aix\u00F2 et permetr\u00E0 crear classes i convidar alumnes.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Hi ha canvis no guardats, segur que voleu sortir d'aquesta p\u00E0gina?\"\n },\n \"cs\": {\n \"CheckInternet\": \"Pros\u00EDm, zkontroluj sv\u00E9 p\u0159ipojen\u00ED k Internetu.\",\n \"Connection_error\": \"Nelze se p\u0159ipojit k serveru.\",\n \"Empty_output\": \"Tento k\u00F3d funguje, ale nem\u00E1 \u017E\u00E1dn\u00FD v\u00FDstup. Pou\u017Eij p\u0159\u00EDkaz print do k\u00F3du nebo pou\u017Eij \u017Eelvu k z\u00EDsk\u00E1n\u00ED v\u00FDstupu.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"P\u0159i prov\u00E1d\u011Bn\u00ED programu se n\u011Bco pokazilo.\",\n \"Other_error\": \"Jejda! Asi jsme n\u011Bkde ud\u011Blali men\u0161\u00ED chybu.\",\n \"Program_repair\": \"Tohle by mohl b\u00FDt spr\u00E1vn\u00FD z\u00E1pis k\u00F3du, m\u016F\u017Ee\u0161 to opravit?\",\n \"Program_too_long\": \"Tv\u016Fj program b\u011B\u017E\u00ED p\u0159\u00EDl\u00ED\u0161 dlouho.\",\n \"ServerError\": \"Napsal*a jsi program, kter\u00FD jsme ne\u010Dekali. Pokud pot\u0159ebuje\u0161 pomoc, po\u0161li n\u00E1m e-mail s \u010D\u00EDslem \u00FArovn\u011B a sv\u00FDm k\u00F3dem na hello@hedy.org. Mezit\u00EDm m\u016F\u017Ee\u0161 zkusit \u00FAkol vy\u0159e\u0161it n\u011Bjak jinak nebo se znova pod\u00EDvat na p\u0159\u00EDklady. D\u00EDky!\",\n \"Transpile_error\": \"Nelze spustit tv\u016Fj program.\",\n \"Transpile_success\": \"Dobr\u00E1 pr\u00E1ce!\\n\u00DA\u017Easn\u00E9!\\nSkv\u011Bl\u00E9!\\nV\u00FDte\u010Dn\u00E9!\\nVede\u0161 si v\u00FDborn\u011B!\",\n \"Transpile_warning\": \"Pozor!\",\n \"Unsaved_Changes\": \"Tv\u016Fj program nen\u00ED ulo\u017Een\u00FD. Chce\u0161 odej\u00EDt bez ulo\u017Een\u00ED?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Zkop\u00EDrovat odkaz ke sd\u00EDlen\u00ED\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u00DArove\u0148\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"cy\": {\n \"CheckInternet\": \"Edrychwch os yw eich cysylltiad rhyngrwyd yn gweithio'n iawn.\",\n \"Connection_error\": \"Ni allem gyrraedd y gweinydd.\",\n \"Empty_output\": \"Mae'r cod hwn yn gweithio ond nid yw'n argraffu unrhyw beth. Ychwanegwch orchymyn argraffu i'ch cod neu defnyddiwch y crwban i gael allbwn.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Aeth rhywbeth o'i le wrth redeg y rhaglen.\",\n \"Other_error\": \"\u0174ps! Efallai ein bod wedi gwneud camgymeriad bach.\",\n \"Program_repair\": \"Gallai hwn fod y cod cywir, allwch chi ei drwsio?\",\n \"Program_too_long\": \"Mae eich rhaglen yn cymryd gormod o amser i'w rhedeg.\",\n \"ServerError\": \"Fe wnaethoch chi ysgrifennu rhaglen nad oeddem yn ei disgwyl. Os ydych chi eisiau helpu, anfonwch e-bost atom gyda'r lefel a'ch rhaglen yn hello@hedy.org. Yn y cyfamser, rhowch gynnig ar rywbeth ychydig yn wahanol ac edrychwch eto ar yr enghreifftiau. Diolch!\",\n \"Transpile_error\": \"Ni allwn redeg eich rhaglen.\",\n \"Transpile_success\": \"Gwaith da!\\nAnhygoel!\\nDa iawn!\\nArdderchog!\\nFe wnaethoch chi waith gwych!\",\n \"Transpile_warning\": \"Rhybudd!\",\n \"Unsaved_Changes\": \"Mae gennych raglen heb ei chadw. Ydych chi eisiau gadael heb ei arbed?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"da\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"de\": {\n \"CheckInternet\": \"Funktioniert deine Internetverbindung.\",\n \"Connection_error\": \"Wir konnten den Server nicht erreichen.\",\n \"Empty_output\": \"Dieses Programm funktioniert aber gibt nichts aus. F\u00FCge ein `print` Befehl hinzu oder benutz die Schildkr\u00F6t um eine Ausgabe zu bekommen.\",\n \"Errors_found\": \"Du hast einen Fehler gemacht! Keine Sorge, wir haben das Programm trotzdem ausgef\u00FChrt\",\n \"Execute_error\": \"Etwas ist beim Ausf\u00FChren des Programms schiefgegangen.\",\n \"Other_error\": \"Hoppla! Vielleicht haben wir einen kleinen Fehler gemacht.\",\n \"Program_repair\": \"Das k\u00F6nnte der korrekte Code sein, kannst du es beheben?\",\n \"Program_too_long\": \"Dein Programm ist zu lang um es auszuf\u00FChren.\",\n \"ServerError\": \"Du hast ein Programm geschrieben das wir nicht erwartet haben. Wenn du uns hlefen m\u00F6chtest, dann sende eine Email an hello@hedy.org und gib darin das Level an in dem du dieses Programm geschrieben hast und f\u00FCge den Programmcode hinzu. In der Zwischenzeit, probiere aus es ein bisschen anders zu formulieren und schau nochmal in die Beispiele. Danke!\",\n \"Transpile_error\": \"Wir konnten Ihr Hedy-Programm nicht lesen.\",\n \"Transpile_success\": \"Gut gemacht!\\nToll!\\nSehr gut gemacht!\\nAusgezeichnet!\\nDas hast du gro\u00DFartig gemacht!\",\n \"Transpile_warning\": \"Warnung!\",\n \"Unsaved_Changes\": \"Du hast ein nicht gespeichertes Programm. M\u00F6chtest du es verwerfen, ohne zu speichern?\",\n \"adventures_restored\": \"Die Standardabenteuer wurden wiederhergestellt.\",\n \"copy_link_to_share\": \"Kopiere Link zum Weitergeben\",\n \"customization_deleted\": \"Anpassungen erfolgreich gel\u00F6scht.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Direkt \u00F6ffnen\",\n \"disabled\": \"Deaktiviert\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Willkommen bei Hedy! Du bist nun eine stolze BesitzerIn eines LehrerIn Kontos, welches dir erlaubt Klassen zu erstellen und Sch\u00FCler einzuladen.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Es gibt ungespeicherte \u00C4nderungen, bist du dir sicher, dass du diese Seite verlassen m\u00F6chtest?\"\n },\n \"el\": {\n \"CheckInternet\": \"\u03A1\u03AF\u03BE\u03B5 \u03BC\u03B9\u03B1 \u03BC\u03B1\u03C4\u03B9\u03AC \u03B1\u03BD \u03B7 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 \u03C3\u03BF\u03C5 \u03C3\u03C4\u03BF \u0394\u03B9\u03B1\u03B4\u03AF\u03BA\u03C4\u03C5\u03BF \u03B4\u03BF\u03C5\u03BB\u03B5\u03CD\u03B5\u03B9 \u03C3\u03C9\u03C3\u03C4\u03AC.\",\n \"Connection_error\": \"\u0394\u03B5\u03BD \u03BC\u03C0\u03BF\u03C1\u03AD\u03C3\u03B1\u03BC\u03B5 \u03BD\u03B1 \u03B2\u03C1\u03BF\u03CD\u03BC\u03B5 \u03C4\u03BF \u03B4\u03B9\u03B1\u03BA\u03BF\u03BC\u03B9\u03C3\u03C4\u03AE.\",\n \"Empty_output\": \"\u0391\u03C5\u03C4\u03CC\u03C2 \u03BF \u03BA\u03CE\u03B4\u03B9\u03BA\u03B1\u03C2 \u03BB\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03B5\u03AF \u03B1\u03BB\u03BB\u03AC \u03B4\u03B5\u03BD \u03B5\u03BC\u03C6\u03B1\u03BD\u03AF\u03B6\u03B5\u03B9 \u03BA\u03AC\u03C4\u03B9. \u03A0\u03C1\u03CC\u03C3\u03B8\u03B5\u03C3\u03B5 \u03BC\u03B9\u03B1 \u03B5\u03BD\u03C4\u03BF\u03BB\u03AE \u03B5\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7\u03C2 \u03C3\u03C4\u03BF\u03BD \u03BA\u03CE\u03B4\u03B9\u03BA\u03AC \u03C3\u03BF\u03C5 \u03AE \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B5 \u03C4\u03B7 \u03C7\u03B5\u03BB\u03CE\u03BD\u03B1 \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03AD\u03C7\u03B5\u03B9\u03C2 \u03AD\u03BE\u03BF\u03B4\u03BF.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u039A\u03AC\u03C4\u03B9 \u03C0\u03AE\u03B3\u03B5 \u03C3\u03C4\u03C1\u03B1\u03B2\u03AC \u03CC\u03C3\u03BF \u03B5\u03BA\u03C4\u03B5\u03BB\u03BF\u03CD\u03C3\u03B1\u03BC\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1.\",\n \"Other_error\": \"\u039F\u03C5\u03C0\u03C2! \u038A\u03C3\u03C9\u03C2 \u03BD\u03B1 \u03BA\u03AC\u03BD\u03B1\u03BC\u03B5 \u03BA\u03AC\u03C0\u03BF\u03B9\u03BF \u03BB\u03B1\u03B8\u03AC\u03BA\u03B9.\",\n \"Program_repair\": \"\u0391\u03C5\u03C4\u03CC\u03C2 \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BF \u03C3\u03C9\u03C3\u03C4\u03CC\u03C2 \u03BA\u03CE\u03B4\u03B9\u03BA\u03B1\u03C2, \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF\u03C2 \u03BD\u03B1 \u03C4\u03BF\u03BD \u03B4\u03B9\u03BF\u03C1\u03B8\u03CE\u03C3\u03B5\u03B9\u03C2;\",\n \"Program_too_long\": \"\u03A4\u03BF \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03AC \u03C3\u03BF\u03C5 \u03C0\u03B1\u03AF\u03C1\u03BD\u03B5\u03B9 \u03C0\u03BF\u03BB\u03CD \u03C7\u03C1\u03CC\u03BD\u03BF \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03B5\u03BA\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03B5\u03AF.\",\n \"ServerError\": \"\u0388\u03B3\u03C1\u03B1\u03C8\u03B5\u03C2 \u03AD\u03BD\u03B1 \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B4\u03B5\u03BD \u03C0\u03B5\u03C1\u03B9\u03BC\u03AD\u03BD\u03B1\u03BC\u03B5. \u0391\u03BD \u03B8\u03AD\u03BB\u03B5\u03C4\u03B5 \u03BD\u03B1 \u03B2\u03BF\u03B7\u03B8\u03AE\u03C3\u03B5\u03B9\u03C2, \u03C3\u03C4\u03B5\u03AF\u03BB\u03B5 \u03BC\u03B1\u03C2 \u03AD\u03BD\u03B1 email \u03BC\u03B5 \u03C4\u03BF \u03B5\u03C0\u03AF\u03C0\u03B5\u03B4\u03BF \u03BA\u03B1\u03B9 \u03C4\u03BF \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03AC \u03C3\u03BF\u03C5 at hello@hedy.org. \u0395\u03BD \u03C4\u03C9 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD, \u03B4\u03BF\u03BA\u03AF\u03BC\u03B1\u03C3\u03B5 \u03BA\u03AC\u03C4\u03B9 \u03BB\u03AF\u03B3\u03BF \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03B5\u03C4\u03B9\u03BA\u03CC \u03BA\u03B1\u03B9 \u03C1\u03AF\u03BE\u03B5 \u03AC\u03BB\u03BB\u03B7 \u03BC\u03B9\u03B1 \u03BC\u03B1\u03C4\u03B9\u03AC \u03C3\u03C4\u03B1 \u03C0\u03B1\u03C1\u03B1\u03B4\u03B5\u03AF\u03B3\u03BC\u03B1\u03C4\u03B1. \u0395\u03C5\u03C7\u03B1\u03C1\u03B9\u03C3\u03C4\u03CE!\",\n \"Transpile_error\": \"\u0394\u03B5\u03BD \u03BC\u03C0\u03BF\u03C1\u03BF\u03CD\u03BC\u03B5 \u03BD\u03B1 \u03B5\u03BA\u03C4\u03B5\u03BB\u03AD\u03C3\u03BF\u03C5\u03BC\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03AC \u03C3\u03BF\u03C5.\",\n \"Transpile_success\": \"\u039A\u03B1\u03BB\u03AE \u03B4\u03BF\u03C5\u03BB\u03B5\u03B9\u03AC!\\n\u0395\u03BE\u03B1\u03B9\u03C1\u03B5\u03C4\u03B9\u03BA\u03AC!\\n\u039C\u03C0\u03C1\u03AC\u03B2\u03BF!\\n\u03A5\u03C0\u03AD\u03C1\u03BF\u03C7\u03B1!\\n\u03A4\u03B1 \u03C0\u03AE\u03B3\u03B5\u03C2 \u03C5\u03C0\u03AD\u03C1\u03BF\u03C7\u03B1!\",\n \"Transpile_warning\": \"\u03A0\u03C1\u03BF\u03C3\u03BF\u03C7\u03AE!\",\n \"Unsaved_Changes\": \"\u0388\u03C7\u03B5\u03B9\u03C2 \u03AD\u03BD\u03B1 \u03BC\u03B7 \u03B1\u03C0\u03BF\u03B8\u03B7\u03BA\u03B5\u03C5\u03BC\u03AD\u03BD\u03BF \u03C0\u03C1\u03CC\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1. \u0398\u03AD\u03BB\u03B5\u03B9\u03C2 \u03BD\u03B1 \u03B1\u03C0\u03BF\u03C7\u03C9\u03C1\u03AE\u03C3\u03B5\u03B9\u03C2 \u03C7\u03C9\u03C1\u03AF\u03C2 \u03BD\u03B1 \u03C4\u03BF \u03B1\u03C0\u03BF\u03B8\u03B7\u03BA\u03B5\u03CD\u03C3\u03B5\u03B9\u03C2;\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"\u0391\u03BD\u03C4\u03AF\u03B3\u03C1\u03B1\u03C8\u03B5 \u03C4\u03BF\u03BD \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03BC\u03BF \u03B3\u03B9\u03B1 \u03BA\u03BF\u03B9\u03BD\u03AE \u03C7\u03C1\u03AE\u03C3\u03B7\",\n \"customization_deleted\": \"\u039F\u03B9 \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AD\u03C2 \u03B4\u03B9\u03B1\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B1\u03BD \u03BC\u03B5 \u03B5\u03C0\u03B9\u03C4\u03C5\u03C7\u03AF\u03B1.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u0386\u03BC\u03B5\u03C3\u03B1 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03CC\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u0395\u03C0\u03AF\u03C0\u03B5\u03B4\u03BF\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"\u039A\u03B1\u03BB\u03CE\u03C2 \u03AE\u03C1\u03B8\u03B5\u03C2 \u03C3\u03C4\u03B7 Hedy! \u0395\u03AF\u03C3\u03B1\u03B9 \u03C0\u03BB\u03AD\u03BF\u03BD \u03C0\u03B5\u03C1\u03AE\u03C6\u03B1\u03BD\u03BF\u03C2 \u03BA\u03AC\u03C4\u03BF\u03C7\u03BF\u03C2 \u03B5\u03BD\u03CC\u03C2 \u03BB\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD \u03BA\u03B1\u03B8\u03B7\u03B3\u03B7\u03C4\u03AE \u03C0\u03BF\u03C5 \u03C3\u03BF\u03C5 \u03B5\u03C0\u03B9\u03C4\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B4\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03B5\u03AF\u03C2 \u03BC\u03B1\u03B8\u03AE\u03BC\u03B1\u03C4\u03B1 \u03BA\u03B1\u03B9 \u03BD\u03B1 \u03C0\u03C1\u03BF\u03C3\u03BA\u03B1\u03BB\u03B5\u03AF\u03C2 \u03BC\u03B1\u03B8\u03B7\u03C4\u03AD\u03C2.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"en\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, we still ran the program\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored.\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"eo\": {\n \"CheckInternet\": \"Kontrolu, \u0109u via Interreta konekto funkcias \u011Duste.\",\n \"Connection_error\": \"Ni ne povis atingi la servilon.\",\n \"Empty_output\": \"Tiu kodo funkcias sed presas nenion. Enmetu presan komandon en vian kodon, a\u016D uzu la testudon por vidi ion.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Io misfunkciis dum rulado de la programo.\",\n \"Other_error\": \"Ho! Eble ni iomete eraris.\",\n \"Program_repair\": \"Tio povus esti la \u011Dusta kodo; \u0109u vi povas ripari \u011Din?\",\n \"Program_too_long\": \"Ruli vian programon bezonas troon da tempo.\",\n \"ServerError\": \"Vi verkis programon, kiun ni ne atendis. Se vi volas helpi, sendu al ni retmesa\u011Don menciantan la nivelon kaj vian programon \u0109e hello@hedy.org. Dume, provu ion alietan, kaj rerigardu la ekzemplojn. Dankon!\",\n \"Transpile_error\": \"Ni ne povas ruli vian programon.\",\n \"Transpile_success\": \"Bona laboro!\\nMirinda!\\nBone farita!\\nBonega!\\nVi bonege faris!\",\n \"Transpile_warning\": \"Atentu!\",\n \"Unsaved_Changes\": \"Via programo estas ne konservita. \u0108u vi volas foriri sen konservi \u011Din?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Rekte malfermi\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Nivelo\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"es\": {\n \"CheckInternet\": \"Comprueba si tu conexi\u00F3n a Internet funciona.\",\n \"Connection_error\": \"No se ha podido conectar con el servidor.\",\n \"Empty_output\": \"Este c\u00F3digo funciona pero no imprime nada. Agrega un comando print a tu c\u00F3digo o usa una tortuga para mostrar salida.\",\n \"Errors_found\": \"\u00A1Has cometido un error! No te preocupes, seguimos ejecutando el programa\",\n \"Execute_error\": \"Algo sali\u00F3 mal mientras se ejecutaba el programa.\",\n \"Other_error\": \"\u00A1Ups! Quiz\u00E1s hemos cometido un peque\u00F1o error.\",\n \"Program_repair\": \"Este podr\u00EDa ser c\u00F3digo correcto, \u00BFlo puedes arreglar?\",\n \"Program_too_long\": \"Tu programa toma demasiado tiempo en ejecutarse.\",\n \"ServerError\": \"Escribiste un programa que no esper\u00E1bamos. Si quieres ayudar, env\u00EDanos un email con el nivel y tu programa a hello@hedy.org. Mientras tanto, intenta algo un poco diferente y \u00E9chale otro vistazo a los ejemplos. \u00A1Gracias!\",\n \"Transpile_error\": \"No podemos ejecutar este programa.\",\n \"Transpile_success\": \"\u00A1Buen trabajo!\\n\u00A1Increible!\\n\u00A1Bien hecho!\\n\u00A1Excelente!\\n\u00A1Lo hiciste genial!\",\n \"Transpile_warning\": \"\u00A1Cuidado!\",\n \"Unsaved_Changes\": \"Tu programa no se ha guardado. \u00BFDeseas irte sin guardarlo?\",\n \"adventures_restored\": \"Se han restaurado las aventuras por defecto.\",\n \"copy_link_to_share\": \"Copiar enlace para compartir\",\n \"customization_deleted\": \"Personalizaci\u00F3n eliminada.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directamente abierto\",\n \"disabled\": \"Deshabilitado\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Nivel\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"\u00A1Bienvenido/a a Hedy! Tu cuenta es de tipo profesor, por lo que puedes crear clases e invitar estudiantes.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Hay cambios sin guardar, \u00BFest\u00E1s seguro de que quieres abandonar esta p\u00E1gina?\"\n },\n \"et\": {\n \"CheckInternet\": \"Kontrolli kas su interneti\u00FChendus t\u00F6\u00F6tab korralikult.\",\n \"Connection_error\": \"Me ei saanud serveriga \u00FChendust.\",\n \"Empty_output\": \"See programm t\u00F6\u00F6tab, aga ei kirjuta midagi ekraanile. Lisa printimise k\u00E4sk oma koodi v\u00F5i kasuta kilpkonna, et saada v\u00E4ljundit.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Midagi l\u00E4ks programmi k\u00E4ivitamisel valesti.\",\n \"Other_error\": \"Oih! V\u00F5ib-olla me tegime programmis v\u00E4ikese vea.\",\n \"Program_repair\": \"See programm saaks olla \u00F5ige, kas sa oskad seda parandada?\",\n \"Program_too_long\": \"Sinu programm v\u00F5tab p\u00E4rast k\u00E4ivitamist liiga kaua aega.\",\n \"ServerError\": \"Sa oled kirjutanud sellise programmi, mida me ei oodanud. Kui sa tahad meid aidata, siis saada meil aadressile hello@hedy.org. Kirjuta meili oma tase ja programm. Samal ajal proovi oma programmi natukene muuta, et sa saaksid edasi minna. Vaata uuesti n\u00E4idiseid, kui on vaja. Suur t\u00E4nu!\",\n \"Transpile_error\": \"Me ei saa sinu programmi k\u00E4ivitada.\",\n \"Transpile_success\": \"Tubli!\\nV\u00E4ga tubli!\\nSuurep\u00E4rane!\\nSuper!\\nV\u00E4ga v\u00E4ga tubli!\",\n \"Transpile_warning\": \"Hoiatus!\",\n \"Unsaved_Changes\": \"Sinu programm ei ole salvestatud. Kas sa tahad lahkuda ilma seda salvestamata?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Tere tulemast Hedy juurde! Sul on n\u00FC\u00FCd \u00F5petaja konto. See annab sulle v\u00F5imaluse klasse luua ja \u00F5pilasi \u00FChinema kutsuda.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"fa\": {\n \"CheckInternet\": \"\u0628\u0628\u06CC\u0646 \u0622\u06CC\u0627 \u0627\u062A\u0635\u0627\u0644 \u0627\u06CC\u0646\u062A\u0631\u0646\u062A \u0634\u0645\u0627 \u0628\u0647 \u062F\u0631\u0633\u062A\u06CC \u06A9\u0627\u0631 \u0645\u06CC \u06A9\u0646\u062F.\",\n \"Connection_error\": \"\u0646\u0645\u06CC\u062A\u0648\u0646\u06CC\u0645 \u0628\u0647 \u0633\u0631\u0648\u0631 \u062F\u0633\u062A\u0631\u0633\u06CC \u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u06CC\u0645.\",\n \"Empty_output\": \"\u0627\u06CC\u0646 \u06A9\u062F \u06A9\u0627\u0631 \u0645\u06CC \u06A9\u0646\u062F \u0627\u0645\u0627 \u0686\u06CC\u0632\u06CC \u0631\u0627 \u0686\u0627\u067E \u0646\u0645\u06CC \u06A9\u0646\u062F. \u06CC\u06A9 \u062F\u0633\u062A\u0648\u0631 \u0686\u0627\u067E \u0631\u0627 \u0628\u0647 \u06A9\u062F \u062E\u0648\u062F \u0627\u0636\u0627\u0641\u0647 \u06A9\u0646 \u06CC\u0627 \u0627\u0632 \u0644\u0627\u06A9 \u067E\u0634\u062A \u0628\u0631\u0627\u06CC \u062E\u0631\u0648\u062C\u06CC \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u0647\u0646\u06AF\u0627\u0645 \u0627\u062C\u0631\u0627\u06CC \u0628\u0631\u0646\u0627\u0645\u0647 \u0645\u0634\u06A9\u0644\u06CC \u067E\u06CC\u0634 \u0622\u0645\u062F.\",\n \"Other_error\": \"\u0627\u0648\u0647! \u0645\u0645\u06A9\u0646\u0647 \u0645\u0627 \u06CC\u06A9 \u0627\u0634\u062A\u0628\u0627\u0647 \u06A9\u0648\u0686\u06A9\u06CC \u06A9\u0631\u062F\u06CC\u0645.\",\n \"Program_repair\": \"\u0627\u06CC\u0646 \u0645\u06CC\u062A\u0648\u0646\u0647 \u06A9\u062F \u0635\u062D\u06CC\u062D \u0628\u0627\u0634\u0647\u060C \u0645\u06CC\u062A\u0648\u0646\u06CC \u062F\u0631\u0633\u062A\u0634 \u06A9\u0646\u06CC\u061F\",\n \"Program_too_long\": \"\u0627\u062C\u0631\u0627\u06CC \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u062A \u062E\u06CC\u0644\u06CC \u0637\u0648\u0644 \u0645\u06CC \u06A9\u0634\u0647.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"\u0646\u0645\u06CC\u062A\u0648\u0646\u06CC \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u062A \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC.\",\n \"Transpile_success\": \"\u0622\u0641\u0631\u06CC\u0646!\\n\u0634\u06AF\u0641\u062A \u0627\u0646\u06AF\u064A\u0632\u0647!\\n\u0622\u0641\u0631\u06CC\u0646!\\n\u0639\u0627\u0644\u06CC!\\n\u06A9\u0627\u0631\u062A \u0639\u0627\u0644\u06CC \u0628\u0648\u062F!\",\n \"Transpile_warning\": \"\u0647\u0634\u062F\u0627\u0631!\",\n \"Unsaved_Changes\": \"\u06CC\u06A9 \u0628\u0631\u0646\u0627\u0645\u0647 \u0630\u062E\u06CC\u0631\u0647 \u0646\u0634\u062F\u0647 \u062F\u0627\u0631\u06CC. \u0622\u06CC\u0627 \u0645\u06CC \u062E\u0648\u0627\u0647\u06CC \u0628\u062F\u0648\u0646 \u0630\u062E\u06CC\u0631\u0647 \u0622\u0646 \u0631\u0627 \u062A\u0631\u06A9 \u06A9\u0646\u06CC\u061F\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"fi\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"fr\": {\n \"CheckInternet\": \"V\u00E9rifie que ta connexion Internet fonctionne correctement.\",\n \"Connection_error\": \"Nous n\u2019avons pas r\u00E9ussi \u00E0 contacter le serveur.\",\n \"Empty_output\": \"Ce code fonctionne mais n'imprime rien. Ajoute une commande d'affichage \u00E0 ton code ou utilise la tortue pour obtenir un r\u00E9sultat.\",\n \"Errors_found\": \"Tu as fait une erreur\u00A0! Ne t'inqui\u00E8te pas, nous avons ex\u00E9cut\u00E9 quand m\u00EAme le programme\",\n \"Execute_error\": \"Quelque chose s\u2019est mal pass\u00E9 en ex\u00E9cutant ce programme.\",\n \"Other_error\": \"Oups\u00A0! Peut-\u00EAtre que nous faisons une petite erreur.\",\n \"Program_repair\": \"Ceci pourrait \u00EAtre le bon code, peux-tu le r\u00E9parer\u00A0?\",\n \"Program_too_long\": \"Ton programme prend trop de temps \u00E0 s'ex\u00E9cuter.\",\n \"ServerError\": \"Tu as \u00E9crit un programme que nous n'attendions pas. Si tu veux nous aider, envoie-nous un email avec le niveau et ton programme \u00E0 hello@hedy.org. En attendant, essaye quelque chose d'un peu diff\u00E9rent et regarde \u00E0 nouveau les exemples. Merci\u00A0!\",\n \"Transpile_error\": \"Nous ne pouvons pas ex\u00E9cuter ton programme.\",\n \"Transpile_success\": \"Bon travail\u00A0!\\nFantastique\u00A0!\\nBien jou\u00E9\u00A0!\\nExcellent\u00A0!\\nTu as fait du bon travail\u00A0!\",\n \"Transpile_warning\": \"Attention\u00A0!\",\n \"Unsaved_Changes\": \"Le programme en cours n'a pas \u00E9t\u00E9 sauvegard\u00E9. Souhaites-tu vraiment quitter sans le sauvegarder\u00A0?\",\n \"adventures_restored\": \"Les aventures par d\u00E9faut ont \u00E9t\u00E9 r\u00E9tablies.\",\n \"copy_link_to_share\": \"Copier le lien \u00E0 partager\",\n \"customization_deleted\": \"Modifications effac\u00E9es avec succ\u00E8s.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Ouvrir imm\u00E9diatement\",\n \"disabled\": \"D\u00E9sactiv\u00E9\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Niveau\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Bienvenue chez Hedy ! Vous \u00EAtes maintenant l'heureux propri\u00E9taire d'un compte enseignant qui vous permet de cr\u00E9er des cours et d'inviter des \u00E9tudiants.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Il y a des changements non pris en comptes, es-tu sur de vouloir quitter la page ?\"\n },\n \"fy\": {\n \"CheckInternet\": \"Wolst eefkes kontrolearje oft dyn internet it noch wol docht.\",\n \"Connection_error\": \"Der is in ferbiningsprobleem. It leit f\u00EAst oan \u00FAs, mar miskien docht dyn internet it eefkes net.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"It draaien fan de koade gong niet hielendal goed.\",\n \"Other_error\": \"Krammele! Dat gong net goed, wy ha sels f\u00EAst in programmearflater makke.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"Hast in programma skreaun dat wy net ferwachte hienen. Ast \u00FAs helpe wolst, stjoer \u00FAs dan in mailtsje mei in level en dyn programma nei hello@hedy.org. Besykje dyn programma in bytsje oan te passen om fierder te gean en sjochris nei de foarbylden. Dankewol!\",\n \"Transpile_error\": \"Dyn koade wie net hielendal goed.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Tink derom!\",\n \"Unsaved_Changes\": \"Dyn programma is net bewarre. Wolst fuortgean s\u00FBnder it te bewarjen?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Diellink kopi\u00EBare\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"he\": {\n \"CheckInternet\": \"\u05D1\u05D3\u05E7\u05D5 \u05D0\u05DD \u05D7\u05D9\u05D1\u05D5\u05E8 \u05D4\u05D0\u05D9\u05E0\u05D8\u05E8\u05E0\u05D8 \u05E9\u05DC\u05DB\u05DD.\u05DF \u05E4\u05D5\u05E2\u05DC \u05DB\u05E8\u05D0\u05D5\u05D9.\",\n \"Connection_error\": \"\u05DC\u05D0 \u05D4\u05E6\u05DC\u05D7\u05E0\u05D5 \u05DC\u05D4\u05D2\u05D9\u05E2 \u05DC\u05E9\u05E8\u05EA.\",\n \"Empty_output\": \"\u05D4\u05E7\u05D5\u05D3 \u05D4\u05D6\u05D4 \u05E2\u05D5\u05D1\u05D3 \u05D0\u05D1\u05DC \u05DC\u05D0 \u05DE\u05D3\u05E4\u05D9\u05E1 \u05E9\u05D5\u05DD \u05D3\u05D1\u05E8. \u05D4\u05D5\u05E1\u05D9\u05E4\u05D5 \u05E4\u05E7\u05D5\u05D3\u05EA \u05D4\u05D3\u05E4\u05E1\u05D4 \u05DC\u05E7\u05D5\u05D3 \u05D0\u05D5 \u05D4\u05E9\u05EA\u05DE\u05E9\u05D5 \u05D1\u05E6\u05D1 \u05D1\u05E9\u05D1\u05D9\u05DC \u05DC\u05E7\u05D1\u05DC \u05E4\u05DC\u05D8.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u05DE\u05E9\u05D4\u05D5 \u05D4\u05E9\u05EA\u05D1\u05E9 \u05D1\u05DE\u05D4\u05DC\u05DA \u05D4\u05E8\u05E6\u05EA \u05D4\u05EA\u05DB\u05E0\u05D9\u05EA.\",\n \"Other_error\": \"\u05D0\u05D5\u05E4\u05E1! \u05D0\u05D5\u05DC\u05D9 \u05E2\u05E9\u05D9\u05E0\u05D5 \u05D8\u05E2\u05D5\u05EA \u05E7\u05D8\u05E0\u05D4.\",\n \"Program_repair\": \"\u05D4\u05E7\u05D5\u05D3 \u05D4\u05D6\u05D4 \u05D9\u05DB\u05D5\u05DC \u05DC\u05D4\u05D9\u05D5\u05EA \u05E0\u05DB\u05D5\u05DF, \u05D4\u05D0\u05DD \u05EA\u05D5\u05DB\u05DC\u05D5 \u05DC\u05EA\u05E7\u05DF \u05D0\u05D5\u05EA\u05D5?\",\n \"Program_too_long\": \"\u05DC\u05EA\u05D5\u05DB\u05E0\u05D9\u05EA \u05E9\u05DC\u05DA \u05DC\u05D5\u05E7\u05D7 \u05D9\u05D5\u05EA\u05E8 \u05DE\u05D3\u05D9 \u05D6\u05DE\u05DF \u05DC\u05E8\u05D5\u05E5.\",\n \"ServerError\": \"\u05DB\u05EA\u05D1\u05EA \u05EA\u05D5\u05DB\u05E0\u05D9\u05EA \u05E9\u05DC\u05D0 \u05E6\u05D9\u05E4\u05D9\u05E0\u05D5 \u05DC\u05D4. \u05D0\u05DD \u05D0\u05EA.\u05D4 \u05E8\u05D5\u05E6\u05D4 \u05E2\u05D6\u05E8\u05D4, \u05E9\u05DC\u05D7.\u05D9 \u05DC\u05E0\u05D5 \u05DE\u05D9\u05D9\u05DC \u05E2\u05DD \u05DE\u05E1\u05E4\u05E8 \u05D4\u05E8\u05DE\u05D4 \u05D5\u05D4\u05EA\u05D5\u05DB\u05E0\u05D9\u05EA \u05E9\u05DC\u05DA \u05DC- hello@hedy.org. \u05D1\u05D9\u05E0\u05EA\u05D9\u05D9\u05DD, \u05E0\u05E1\u05D4.\u05D9 \u05DE\u05E9\u05D4\u05D5 \u05E7\u05E6\u05EA \u05E9\u05D5\u05E0\u05D4 \u05D5\u05D4\u05E1\u05EA\u05DB\u05DC.\u05D9 \u05E2\u05DC \u05D4\u05D3\u05D5\u05D2\u05DE\u05D0\u05D5\u05EA. \u05EA\u05D5\u05D3\u05D4!\",\n \"Transpile_error\": \"\u05D0\u05D9\u05E0\u05E0\u05D5 \u05D9\u05DB\u05D5\u05DC\u05D9\u05DD.\u05D5\u05EA \u05DC\u05D4\u05E8\u05D9\u05E5 \u05D0\u05EA \u05D4\u05EA\u05D5\u05DB\u05E0\u05D9\u05EA \u05E9\u05DC\u05DA.\",\n \"Transpile_success\": \"\u05E2\u05D1\u05D5\u05D3\u05D4 \u05D8\u05D5\u05D1\u05D4!\\n\u05DE\u05D3\u05D4\u05D9\u05DD!\\n\u05DB\u05DC \u05D4\u05DB\u05D1\u05D5\u05D3!\\n\u05DE\u05E6\u05D5\u05D9\u05DF!\\n\u05D4\u05D9\u05D9\u05EA \u05DE\u05E2\u05D5\u05DC\u05D4!\",\n \"Transpile_warning\": \"\u05D0\u05D6\u05D4\u05E8\u05D4!\",\n \"Unsaved_Changes\": \"\u05D9\u05E9 \u05DC\u05DA \u05EA\u05DB\u05E0\u05D9\u05EA \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D4. \u05D4\u05D0\u05DD \u05D1\u05E8\u05E6\u05D5\u05E0\u05DA \u05DC\u05E2\u05D6\u05D5\u05D1 \u05D1\u05DC\u05D9 \u05DC\u05E9\u05DE\u05D5\u05E8 \u05D0\u05D5\u05EA\u05D4?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u05E8\u05DE\u05D4\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"hi\": {\n \"CheckInternet\": \"\u0926\u0947\u0916\u0947\u0902 \u0915\u093F \u0906\u092A\u0915\u093E \u0907\u0902\u091F\u0930\u0928\u0947\u091F \u0915\u0928\u0947\u0915\u094D\u0936\u0928 \u0920\u0940\u0915 \u0938\u0947 \u0915\u093E\u092E \u0915\u0930 \u0930\u0939\u093E \u0939\u0948 \u092F\u093E \u0928\u0939\u0940\u0902.\",\n \"Connection_error\": \"\u0939\u092E \u0938\u0930\u094D\u0935\u0930 \u0924\u0915 \u0928\u0939\u0940\u0902 \u092A\u0939\u0941\u0902\u091A \u0938\u0915\u0947|\",\n \"Empty_output\": \"\u092F\u0939 \u0915\u094B\u0921 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948 \u0932\u0947\u0915\u093F\u0928 \u0915\u0941\u091B \u092D\u0940 \u092A\u094D\u0930\u093F\u0902\u091F \u0928\u0939\u0940\u0902 \u0915\u0930\u0924\u093E \u0939\u0948\u0964 \u0905\u092A\u0928\u0947 \u0915\u094B\u0921 \u092E\u0947\u0902 \u090F\u0915 \u092A\u094D\u0930\u093F\u0902\u091F \u0915\u092E\u093E\u0902\u0921 \u091C\u094B\u0921\u093C\u0947\u0902 \u092F\u093E \u0906\u0909\u091F\u092A\u0941\u091F \u092A\u094D\u0930\u093E\u092A\u094D\u0924 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u091F\u0930\u094D\u091F\u0932 \u0915\u093E \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0947\u0902|\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u091A\u0932\u093E\u0924\u0947 \u0938\u092E\u092F \u0915\u0941\u091B \u0917\u0932\u0924 \u0939\u094B \u0917\u092F\u093E|\",\n \"Other_error\": \"\u0909\u092B\u093C! \u0936\u093E\u092F\u0926 \u0939\u092E\u0928\u0947 \u0925\u094B\u0921\u093C\u0940 \u0938\u0940 \u0917\u0932\u0924\u0940 \u0915\u0930 \u0926\u0940\u0964\",\n \"Program_repair\": \"\u092F\u0939 \u0938\u0939\u0940 \u0915\u094B\u0921 \u0939\u094B \u0938\u0915\u0924\u093E \u0939\u0948, \u0915\u094D\u092F\u093E \u0906\u092A \u0907\u0938\u0947 \u0920\u0940\u0915 \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902?\",\n \"Program_too_long\": \"\u0906\u092A\u0915\u093E \u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u091A\u0932\u0928\u0947 \u092E\u0947\u0902 \u092C\u0939\u0941\u0924 \u0905\u0927\u093F\u0915 \u0938\u092E\u092F \u0932\u0947\u0924\u093E \u0939\u0948\u0964\",\n \"ServerError\": \"\u0906\u092A\u0928\u0947 \u090F\u0915 \u0910\u0938\u093E \u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u0932\u093F\u0916\u093E \u091C\u093F\u0938\u0915\u0940 \u0939\u092E \u0905\u092A\u0947\u0915\u094D\u0937\u093E \u0928\u0939\u0940\u0902 \u0915\u0930 \u0930\u0939\u0947 \u0925\u0947\u0964 \u092F\u0926\u093F \u0906\u092A \u092E\u0926\u0926 \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902, \u0924\u094B \u0939\u092E\u0947\u0902 hello@hedy.org \u092A\u0930 \u0938\u094D\u0924\u0930 \u0914\u0930 \u0905\u092A\u0928\u0947 \u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u0915\u0947 \u0938\u093E\u0925 \u090F\u0915 \u0908\u092E\u0947\u0932 \u092D\u0947\u091C\u0947\u0902\u0964 \u0907\u0938 \u092C\u0940\u091A, \u0915\u0941\u091B \u0905\u0932\u0917 \u0915\u0930\u0928\u0947 \u0915\u0940 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902 \u0914\u0930 \u0909\u0926\u093E\u0939\u0930\u0923\u094B\u0902 \u092A\u0930 \u090F\u0915 \u0914\u0930 \u0928\u091C\u093C\u0930 \u0921\u093E\u0932\u0947\u0902\u0964 \u0927\u0928\u094D\u092F\u0935\u093E\u0926!\",\n \"Transpile_error\": \"\u0939\u092E \u0906\u092A\u0915\u093E \u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u0928\u0939\u0940\u0902 \u091A\u0932\u093E \u0938\u0915\u0924\u0947\u0964\",\n \"Transpile_success\": \"\u0905\u091A\u094D\u091B\u093E \u0915\u093E\u0930\u094D\u092F!\\n\u0905\u0926\u094D\u092D\u0941\u0924!\\n\u092C\u0939\u0941\u0924 \u092C\u0922\u093C\u093F\u092F\u093E!\\n\u0909\u0924\u094D\u0915\u0943\u0937\u094D\u091F!\\n\u0906\u092A\u0928\u0947 \u092C\u0939\u0941\u0924 \u0905\u091A\u094D\u091B\u093E \u0915\u093F\u092F\u093E!\",\n \"Transpile_warning\": \"\u091A\u0947\u0924\u093E\u0935\u0928\u0940!\",\n \"Unsaved_Changes\": \"\u0906\u092A\u0915\u0947 \u092A\u093E\u0938 \u090F\u0915 \u0938\u0939\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u0917\u092F\u093E \u092A\u094D\u0930\u094B\u0917\u094D\u0930\u093E\u092E \u0939\u0948\u0964 \u0915\u094D\u092F\u093E \u0906\u092A \u0907\u0938\u0947 \u0938\u0939\u0947\u091C\u0947 \u092C\u093F\u0928\u093E \u091B\u094B\u0921\u093C\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"\u0936\u0947\u092F\u0930 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0932\u093F\u0902\u0915 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902\",\n \"customization_deleted\": \"\u0905\u0928\u0941\u0915\u0942\u0932\u0928 \u0938\u092B\u0932\u0924\u093E\u092A\u0942\u0930\u094D\u0935\u0915 \u0939\u091F\u093E \u0926\u093F\u090F \u0917\u090F\u0964\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u0938\u094D\u0924\u0930\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"\u0939\u0947\u0921\u0940 \u092E\u0947\u0902 \u0906\u092A\u0915\u093E \u0938\u094D\u0935\u093E\u0917\u0924 \u0939\u0948! \u0905\u092C \u0906\u092A \u090F\u0915 \u0936\u093F\u0915\u094D\u0937\u0915 \u0916\u093E\u0924\u0947 \u0915\u0947 \u0917\u0930\u094D\u0935\u093F\u0924 \u0938\u094D\u0935\u093E\u092E\u0940 \u0939\u0948\u0902 \u091C\u094B \u0906\u092A\u0915\u094B \u0915\u0915\u094D\u0937\u093E\u090F\u0902 \u092C\u0928\u093E\u0928\u0947 \u0914\u0930 \u091B\u093E\u0924\u094D\u0930\u094B\u0902 \u0915\u094B \u0906\u092E\u0902\u0924\u094D\u0930\u093F\u0924 \u0915\u0930\u0928\u0947 \u0915\u0940 \u0905\u0928\u0941\u092E\u0924\u093F \u0926\u0947\u0924\u093E \u0939\u0948\u0964\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"hu\": {\n \"CheckInternet\": \"Ellen\u0151rizd, hogy az internetkapcsolat megfelel\u0151en m\u0171k\u00F6dik-e!\",\n \"Connection_error\": \"Nem tudjuk el\u00E9rni a szervert.\",\n \"Empty_output\": \"M\u0171k\u00F6dik a program, de nem \u00EDr ki semmit. Helyezz el egy ki\u00EDr\u00E1st a k\u00F3dban, vagy haszn\u00E1ld a tekn\u0151st, hogy kimenetet kapj!\",\n \"Errors_found\": \"Hib\u00E1t k\u00F6vett\u00E9l el! Ne agg\u00F3djon, m\u00E9g mindig futtatjuk a programot\",\n \"Execute_error\": \"Hiba t\u00F6rt\u00E9nt a program futtat\u00E1sa k\u00F6zben.\",\n \"Other_error\": \"Hopp\u00E1! Tal\u00E1n elk\u00F6vett\u00FCnk egy kis hib\u00E1t.\",\n \"Program_repair\": \"Ez lehet a helyes k\u00F3d, meg tudod jav\u00EDtani?\",\n \"Program_too_long\": \"T\u00FAl hossz\u00FA ideig tart programod futtat\u00E1sa.\",\n \"ServerError\": \"Olyan programot \u00EDrt\u00E1l, amire nem sz\u00E1m\u00EDtottunk. Ha seg\u00EDteni szeretn\u00E9l, k\u00FCldj nek\u00FCnk egy emailt a szinttel \u00E9s a programmal a hello@hedy.org c\u00EDmre! Id\u0151k\u00F6zben pr\u00F3b\u00E1ld ki egy kicsit m\u00E1sk\u00E9pp, \u00E9s n\u00E9zd meg a p\u00E9ld\u00E1kat! K\u00F6sz\u00F6nj\u00FCk!\",\n \"Transpile_error\": \"Nem tudjuk futtatni a k\u00F3dodat.\",\n \"Transpile_success\": \"Sz\u00E9p munka!\\nLeny\u0171g\u00F6z\u0151!\\n\u00DCgyes!\\nKit\u0171n\u0151!\\nRemek\u00FCl csin\u00E1ltad!\",\n \"Transpile_warning\": \"Vigy\u00E1zat!\",\n \"Unsaved_Changes\": \"Nem mentett programod van. El akarsz menni ment\u00E9s n\u00E9lk\u00FCl?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Link m\u00E1sol\u00E1sa a megoszt\u00E1shoz\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Szint\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\uD83D\uDDFB, \uD83D\uDCDC, \u2702\uFE0F\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Biztosan elhagyod ezt az oldalt? Mentetlen v\u00E1ltoztat\u00E1said vannak, amik \u00EDgy elveszhetnek.\"\n },\n \"id\": {\n \"CheckInternet\": \"Cobalah cek apakah internet kamu berjalan dengan baik.\",\n \"Connection_error\": \"Kami tidak bisa terhubung dengan server.\",\n \"Empty_output\": \"Kode ini berfungsi tetapi tidak mencetak apa pun. Tambahkan print command ke dalam kode Anda atau gunakan turtle untuk mendapatkan hasil keluaran.\",\n \"Errors_found\": \"Kamu membuat kesalahan! Tidak perlu khawatir, program tetap berjalan\",\n \"Execute_error\": \"Sesuatu berjalan tidak seharusnya ketika program sedang dijalankan.\",\n \"Other_error\": \"Ups! Mungkin kamu membuat kesalahan kecil.\",\n \"Program_repair\": \"Apakah ini bisa menjadi kode yang benar, bisakah kamu memperbaikinya?\",\n \"Program_too_long\": \"Program Anda memakan waktu terlalu lama untuk berjalan.\",\n \"ServerError\": \"Kamu menuliskan sebuah program yang tidak kami duga. Jika kamu butuh bantuan, kirimkan email dengan informasi level dan program kamu ke hello@hedy.org. Sembari menunggu respon, cobalah cara lain atau lihat kembali contoh-contoh yang disediakan. Terima kasih!\",\n \"Transpile_error\": \"Kami tidak bisa menjalankan kode kamu.\",\n \"Transpile_success\": \"Kerja bagus!\\nKeren!\\nBagus sekali!\\nBagus sekali!\\nKamu sudah bagus!\",\n \"Transpile_warning\": \"Peringatan!\",\n \"Unsaved_Changes\": \"Kamu memiliki program yang belum disimpan. Apakah kamu mau keluar tanpa menyimpannya?\",\n \"adventures_restored\": \"Petualangan default telah dipulihkan.\",\n \"copy_link_to_share\": \"Salin tautan untuk berbagi\",\n \"customization_deleted\": \"Penyesuaian berhasil dihapus.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Terbuka langsung\",\n \"disabled\": \"Dinonaktifkan\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Selamat datang di Hedy! Anda sekarang bangga menjadi pemilik akun guru yang memungkinkan Anda membuat kelas dan mengundang siswa.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Ada perubahan yang belum disimpan, yakin ingin meninggalkan halaman ini?\"\n },\n \"it\": {\n \"CheckInternet\": \"Dai un'occhiata se la tua connessione Internet funziona correttamente.\",\n \"Connection_error\": \"Non siamo riuscito a contattare il server.\",\n \"Empty_output\": \"Questo codice funziona ma non stampa nulla. Aggiungi un comando print al tuo codice o usa la tartaruga per ottenere l'output.\",\n \"Errors_found\": \"Hai fatto un errore! Non preoccuparti, abbiamo comunque eseguito il programma\",\n \"Execute_error\": \"Qualcosa \u00E8 andato storto nell'esecuzione del tuo codice.\",\n \"Other_error\": \"Ops! Forse abbiamo fatto un errore.\",\n \"Program_repair\": \"Questo potrebbe essere il codice corretto, puoi risolverlo?\",\n \"Program_too_long\": \"Il tuo programma impiega troppo tempo per essere eseguito.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"Non possiamo eseguire il tuo programma.\",\n \"Transpile_success\": \"Ottimo lavoro!\\nFantastico!\\nBen fatto!\\nEccellente!\\nSei stato bravissimo!\",\n \"Transpile_warning\": \"Attenzione!\",\n \"Unsaved_Changes\": \"Hai un programma non salvato. Vuoi uscire senza salvare?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"Dado\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"fortuna\",\n \"haunted\": \"infestato\",\n \"level_title\": \"Livello\",\n \"restaurant\": \"Ristorante\",\n \"rock\": \"Roccia\",\n \"songs\": \"Suono\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"Tartaruga\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ja\": {\n \"CheckInternet\": \"\u30A4\u30F3\u30BF\u30FC\u30CD\u30C3\u30C8\u306E\u63A5\u7D9A\u3092\u8ABF\u3079\u3066\u304F\u3060\u3055\u3044\u3002\",\n \"Connection_error\": \"\u30B5\u30FC\u30D0\u306B\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3002\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"\u3042\uFF01\u591A\u5206\u3001\u79C1\u305F\u3061\u306F\u9593\u9055\u3044\u3057\u307E\u3057\u305F\u3002\u3054\u3081\u3093\u306A\u3055\u3044\u3002\",\n \"Program_repair\": \"\u3053\u306E\u30B3\u30FC\u30C9\u306F\u6B63\u89E3\u304C\u3067\u304D\u307E\u3059\u3002\u76F4\u305B\u307E\u3059\u304B\uFF1F\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"\u30D7\u30ED\u30B0\u30E9\u30E0\u3092\u5B9F\u884C\u304C\u3067\u304D\u307E\u305B\u3093\u3002\",\n \"Transpile_success\": \"\u3088\u304F\u3067\u304D\u305F\uFF01\\n\u3059\u3054\u3044\uFF01\\n\u3088\u304F\u3084\u3063\u305F\uFF01\\n\u3048\u3089\u3044\uFF01\\n\u9811\u5F35\u3063\u305F\u306D\uFF01\",\n \"Transpile_warning\": \"\u8B66\u544A\uFF01\",\n \"Unsaved_Changes\": \"\u975E\u4FDD\u5B58\u30D7\u30ED\u30B0\u30E9\u30E0\u304C\u3042\u308A\u307E\u3059\u3002\u305D\u308C\u3067\u3082\u3001\u7D42\u4E86\u3057\u307E\u3059\u304B\uFF1F\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"kmr\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ko\": {\n \"CheckInternet\": \"\uC778\uD130\uB137 \uC5F0\uACB0\uC774 \uC81C\uB300\uB85C \uC791\uB3D9\uD558\uB294\uC9C0 \uD655\uC778\uD574 \uBCF4\uC138\uC694.\",\n \"Connection_error\": \"\uC11C\uBC84\uC5D0 \uC5F0\uACB0\uC774 \uC548 \uB410\uC5B4\uC694.\",\n \"Empty_output\": \"\uC774 \uCF54\uB4DC\uB294 \uC791\uB3D9\uD558\uC9C0\uB9CC \uC544\uBB34\uAC83\uB3C4 \uC778\uC1C4\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF54\uB4DC\uC5D0 \uC778\uC1C4 \uBA85\uB839\uC744 \uCD94\uAC00\uD558\uAC70\uB098 \uAC70\uBD81\uC774\uB97C \uC0AC\uC6A9\uD558\uC5EC \uCD9C\uB825\uC744 \uC5BB\uC73C\uC2ED\uC2DC\uC624.\",\n \"Errors_found\": \"\uD2C0\uB9B0\uAC83 \uAC19\uC544\uC694! \uAC71\uC815 \uB9C8\uC138\uC694, hedy\uB294 \uC2E4\uC218\uB97C \uCC3E\uC73C\uB824 \uD558\uACE0 \uC788\uC5B4\uC694\",\n \"Execute_error\": \"\uD504\uB85C\uADF8\uB7A8\uC744 \uC2E4\uD589\uD558\uB294 \uB3D9\uC548 \uBB38\uC81C\uAC00 \uC0DD\uACBC\uC5B4\uC694.\",\n \"Other_error\": \"\uC774\uB7F0! \uC800\uD76C\uAC00 \uC791\uC740 \uC2E4\uC218\uB97C \uD588\uB098\uBD10\uC694.\",\n \"Program_repair\": \"\uC774\uAC8C \uB9DE\uB294 \uCF54\uB4DC\uC77C \uC218\uB3C4 \uC788\uB294\uB370 \uC218\uC815 \uD574\uC8FC\uC2E4 \uC218 \uC788\uB098\uC694?\",\n \"Program_too_long\": \"\uD504\uB85C\uADF8\uB7A8\uC744 \uC2E4\uD589\uD558\uB294 \uB370 \uC2DC\uAC04\uC774 \uB108\uBB34 \uC624\uB798 \uAC78\uB9AC\uB124\uC694.\",\n \"ServerError\": \"\uB2F9\uC2E0\uC740 \uC800\uD76C\uAC00 \uC608\uC0C1\uD558\uC9C0 \uBABB\uD588\uB358 \uD504\uB85C\uADF8\uB7A8\uC744 \uC791\uC131\uD588\uC2B5\uB2C8\uB2E4. \uB9CC\uC57D \uB3C4\uC6C0\uC744 \uC8FC\uACE0 \uC2F6\uB2E4\uBA74, \uC6B0\uB9AC\uC5D0\uAC8C \uB808\uBCA8\uACFC hello@hedy.org \uC5D0 \uC788\uB294 \uB2F9\uC2E0\uC758 \uD504\uB85C\uADF8\uB7A8\uACFC \uD568\uAED8 \uC774\uBA54\uC77C\uC744 \uBCF4\uB0B4\uC8FC\uC138\uC694. \uADF8 \uB3D9\uC548, \uC870\uAE08 \uB2E4\uB978 \uAC83\uC744 \uC2DC\uB3C4\uD574\uBCF4\uACE0 \uC0AC\uB840\uB97C \uB2E4\uC2DC \uC0B4\uD3B4\uBCF4\uC138\uC694. \uAC10\uC0AC\uD569\uB2C8\uB2E4!\",\n \"Transpile_error\": \"\uD504\uB85C\uADF8\uB7A8\uC744 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\",\n \"Transpile_success\": \"\uC218\uACE0\uD558\uC168\uC5B4\uC694!\\n\uCD5C\uACE0\uC5D0\uC694!\\n\uC815\uB9D0 \uC798\uD558\uC168\uC5B4\uC694!\\n\uD6CC\uB96D\uD574\uC694!\\n\uB300\uB2E8\uD574\uC694!\",\n \"Transpile_warning\": \"\uACBD\uACE0!\",\n \"Unsaved_Changes\": \"\uC800\uC7A5\uD558\uC9C0 \uC54A\uC740 \uD504\uB85C\uADF8\uB7A8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uC800\uC7A5\uD558\uC9C0 \uC54A\uACE0 \uB098\uAC00\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?\",\n \"adventures_restored\": \"\uAE30\uBCF8 \uBAA8\uD5D8\uC774 \uBCF5\uAD6C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\",\n \"copy_link_to_share\": \"\uACF5\uC720\uD560 \uB9C1\uD06C \uBCF5\uC0AC\",\n \"customization_deleted\": \"\uC0AC\uC6A9\uC790 \uC9C0\uC815\uC744 \uC131\uACF5\uC801\uC73C\uB85C \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\uC9C1\uC811 \uC5F4\uB9BC\",\n \"disabled\": \"\uBE44\uD65C\uC131\uD654\uB428\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\uB808\uBCA8\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"mi\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, we still ran the program\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored.\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"nl\": {\n \"CheckInternet\": \"Controleer even of je internetverbinding het nog doet.\",\n \"Connection_error\": \"We konden niet goed met de server praten.\",\n \"Empty_output\": \"Deze code werkt wel, maar print niks. Voeg een print commando toe aan je code of gebruik de schildpad om output te krijgen.\",\n \"Errors_found\": \"Je hebt een foutje gemaakt! Maak je geen zorgen, Hedy probeert de fouten te vinden\",\n \"Execute_error\": \"Er ging iets fout bij het uitvoeren van het programma.\",\n \"Other_error\": \"Oeps! Misschien hebben wij een klein programmeerfoutje gemaakt.\",\n \"Program_repair\": \"Dit kan de correcte code zijn, kan je jouw code repareren?\",\n \"Program_too_long\": \"Jouw programma duurt te lang bij het uitvoeren.\",\n \"ServerError\": \"Je hebt een programma geschreven dat we niet verwacht hadden. Als je wilt helpen, stuur ons dan een mailtje met het level en je programma op hello@hedy.org. Probeer om verder te gaan je programma een beetje aan te passen en kijk nog eens goed naar de voorbeelden. Bedankt!\",\n \"Transpile_error\": \"We konden je code niet goed lezen.\",\n \"Transpile_success\": \"Goed gedaan!\\nGa zo door!\\nTopper!\\nSuper!\\nBravo!\",\n \"Transpile_warning\": \"Let op!\",\n \"Unsaved_Changes\": \"Jouw programma is niet opgeslagen. Wil je weggaan zonder het op te slaan?\",\n \"adventures_restored\": \"De standaardavonturen zijn terug gezet.\",\n \"copy_link_to_share\": \"Kopieer link voor delen\",\n \"customization_deleted\": \"Personalisatie succesvol verwijderd.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Gelijk open\",\n \"disabled\": \"Gedeactiveerd\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welkom bij Hedy! Jouw account is omgezet naar een leerkrachtenaccount. Je kan nu klassen maken en er leerlingen in uitnodigen.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Er zijn wijzigingen nog niet opgeslagen, weet je zeker dat je terug wilt gaan?\"\n },\n \"no_NB\": {\n \"CheckInternet\": \"Sjekk internetttilkoblingen din, fungerer den.\",\n \"Connection_error\": \"Vi fikk ikke kontakt med serveren.\",\n \"Empty_output\": \"Denne koden virker men den skriver ikke ut noe. Legg til en print kommando til koden din eller bruk skipadden til \u00E5 f\u00E5 noe output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Noe gikk feil mens vi kj\u00F8rte programmet.\",\n \"Other_error\": \"Oops! Kanskje vi gjorde en liten feil.\",\n \"Program_repair\": \"Dette kan v\u00E6re det korrekte programmet, kan du fikse det?\",\n \"Program_too_long\": \"Programmet ditt tok for lang tid \u00E5 kj\u00F8re.\",\n \"ServerError\": \"Du skrev et program vi ikke forventet oss. Om du trenger hjelp, send en epost med niv\u00E5et du er p\u00E5 og programmet til hello@hedy.org. I mellomtiden, pr\u00F8v noe annet og ta en titt p\u00E5 eksemplene. Takk!\",\n \"Transpile_error\": \"Vi kan ikke kj\u00F8re programmet ditt.\",\n \"Transpile_success\": \"Godt jobbet!\\nFantastisk!\\nVeldig bra!\\nSupert!\\nDette fikk du til!\",\n \"Transpile_warning\": \"Advarsel!\",\n \"Unsaved_Changes\": \"Du har ulagrede endringer i programmet. Vil du g\u00E5 uten \u00E5 lagre de?\",\n \"adventures_restored\": \"The default adventures have been restored.\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"pa_PK\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"pap\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, we still ran the program\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored.\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"pl\": {\n \"CheckInternet\": \"Upewnij si\u0119, \u017Ce masz po\u0142\u0105czenie z Internetem.\",\n \"Connection_error\": \"B\u0142\u0105d po\u0142\u0105czenia z serwerem.\",\n \"Empty_output\": \"Ten kod dzia\u0142a, ale niczego nie wypisuje. Dodaj komend\u0119 'napisz' lub u\u017Cyj \u017C\u00F3\u0142wia, by otrzyma\u0107 dane wyj\u015Bciowe.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Co\u015B posz\u0142o nie tak w trakcie wykonywania programu.\",\n \"Other_error\": \"Ups! Mo\u017Ce pope\u0142nili\u015Bmy ma\u0142y b\u0142\u0105d.\",\n \"Program_repair\": \"To mo\u017Ce by\u0107 poprawny kod, czy mo\u017Cesz go naprawi\u0107?\",\n \"Program_too_long\": \"Tw\u00F3j program dzia\u0142a zbyt d\u0142ugo.\",\n \"ServerError\": \"Napisa\u0142e\u015B program, jakiego si\u0119 nie spodziewali\u015Bmy. Je\u017Celi chcesz nam pom\u00F3c, wy\u015Blij wiadomo\u015B\u0107 na adres e-mail hello@hedy.org i wska\u017C poziom, na kt\u00F3rym napisa\u0142e\u015B ten program oraz za\u0142\u0105cz jego kod. W mi\u0119dzyczasie spr\u00F3buj czego\u015B innego i ponownie sp\u00F3jrz na przyk\u0142ady. Dzi\u0119ki!\",\n \"Transpile_error\": \"Program nie mo\u017Ce zosta\u0107 uruchomiony.\",\n \"Transpile_success\": \"Dobra robota!\\nWspaniale!\\n\u015Awietnie!\\nDoskonale!\\nDobrze Ci posz\u0142o!\",\n \"Transpile_warning\": \"Ostrze\u017Cenie!\",\n \"Unsaved_Changes\": \"Tw\u00F3j program nie zosta\u0142 zapisany. Chcesz wyj\u015B\u0107 mimo to?\",\n \"adventures_restored\": \"Domy\u015Blne przygody zosta\u0142y przywr\u00F3cone.\",\n \"copy_link_to_share\": \"Skopiuj udost\u0119pniaj\u0105cy link\",\n \"customization_deleted\": \"Personalizacje zostaly usuni\u0119te pomy\u015Blnie.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Zawsze dost\u0119pne\",\n \"disabled\": \"Wy\u0142\u0105czono\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Poziom\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Witaj w Hedy! Jeste\u015B teraz dumnym posiadaczem konta nauczyciela, kt\u00F3re pozwala Ci na tworzenie klas i zapraszanie uczni\u00F3w.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Strona zawiera niezapisane zmiany. Czy jeste\u015B pewien, \u017Ce chcesz wyj\u015B\u0107 bez zapisania zmian?\"\n },\n \"pt_BR\": {\n \"CheckInternet\": \"Verifique se a sua conex\u00E3o com a Internet est\u00E1 funcionando corretamente.\",\n \"Connection_error\": \"N\u00F3s n\u00E3o conseguimos conectar ao servidor.\",\n \"Empty_output\": \"Este c\u00F3digo funciona mas n\u00E3o imprime nada. Adicione um comando de impress\u00E3o ou use a tartaruga para ter alguma sa\u00EDda.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Alguma coisa deu errado enquanto o programa era executado.\",\n \"Other_error\": \"Opa! Talvez n\u00F3s fizemos alguma coisa errada.\",\n \"Program_repair\": \"Este poderia ser o c\u00F3digo correto, voc\u00EA poderia corrigir ele?\",\n \"Program_too_long\": \"O seu programa leva muito tempo para executar.\",\n \"ServerError\": \"Voc\u00EA escreveu um programa que n\u00E3o est\u00E1vamos esperando. Se voc\u00EA quiser ajudar, envie-nos um email com o n\u00EDvel e seu programa para hello@hedy.org. Enquanto isso, tente algo um pouco diferente e d\u00EA uma outra olhada nos exemplos. Obrigado!\",\n \"Transpile_error\": \"N\u00E3o conseguimos executar o seu programa.\",\n \"Transpile_success\": \"Bom trabalho!\\nImpressionante!\\nMuito bem!\\nExcelente!\\n\u00D3timo!\",\n \"Transpile_warning\": \"Aviso!\",\n \"Unsaved_Changes\": \"Voc\u00EA tem um programa n\u00E3o salvo. Voc\u00EA deseja sair sem salv\u00E1-lo?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copiar link para compartilhar\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"N\u00EDvel\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"pt_PT\": {\n \"CheckInternet\": \"Verifica se a tua liga\u00E7\u00E3o de Internet est\u00E1 a funcionar correctamente.\",\n \"Connection_error\": \"N\u00E3o conseguimos contactar o servidor.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Alguma coisa correu mal enquanto execut\u00E1vamos o programa.\",\n \"Other_error\": \"Oops! Talvez cometemos um pequeno erro.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"Escreveste um programa que n\u00E3o est\u00E1vamos \u00E0 espera. Se queres ajudar, envia-nos um email com o n\u00EDvel e o teu programa para hello@hedy.org. Entretanto, experimenta alguma coisa um pouco diferente e volta a olhar para os exemplos. Obrigado!\",\n \"Transpile_error\": \"N\u00E3o conseguimos executar o teu programa.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Aviso!\",\n \"Unsaved_Changes\": \"Tens um programa por guardar. Queres sair sem o guardar?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copiar liga\u00E7\u00E3o de partilha\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"N\u00EDvel\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"tartaruga\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ro\": {\n \"CheckInternet\": \"Verific\u0103 dac\u0103 ai semnal la internet.\",\n \"Connection_error\": \"Nu am putut contacta serverul.\",\n \"Empty_output\": \"Acest program func\u021Bioneaz\u0103 dar nu printeaz\u0103 nimic. Adaug\u0103 o comand\u0103 de printare sau folose\u0219te \u021Bestoasa s\u0103 ob\u021Bii un rezultat.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Ceva nu a func\u021Bionat corect in timpul rul\u0103rii programului.\",\n \"Other_error\": \"Opa! Este posibil s\u0103 fi f\u0103cut vreo gre\u0219eal\u0103.\",\n \"Program_repair\": \"Probabil a\u0219a arat\u0103 programul corect, po\u021Bi corecta?\",\n \"Program_too_long\": \"Rularea programului t\u0103u dureaz\u0103 prea mult.\",\n \"ServerError\": \"Ai scris un program nea\u0219teptat. Dac\u0103 ai nevoie de ajutor trimite-ne un email la: hello@hedy.org \u0219i specific\u0103 nivelul. Intre timp incearc\u0103 ceva diferit \u0219i incearc\u0103 din nou exemplele. Mul\u021Bumim!\",\n \"Transpile_error\": \"Nu putem rula program t\u0103u.\",\n \"Transpile_success\": \"Excelent!\\nExtraordinar!\\nFoarte bine!\\nExcelent!\\nFoarte bine!\",\n \"Transpile_warning\": \"Aten\u021Bie!\",\n \"Unsaved_Changes\": \"Ai un program care nu este salvat. Dore\u0219ti s\u0103 ie\u0219i f\u0103r\u0103 salvare?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ru\": {\n \"CheckInternet\": \"\u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u0432\u043E\u0451 \u0438\u043D\u0442\u0435\u0440\u043D\u0435\u0442 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435.\",\n \"Connection_error\": \"\u041D\u0435 \u043C\u043E\u0433\u0443 \u043D\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440.\",\n \"Empty_output\": \"\u042D\u0442\u043E\u0442 \u043A\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442, \u043D\u043E \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u0442 \u043D\u0438\u0447\u0435\u0433\u043E. \u0414\u043E\u0431\u0430\u0432\u044C \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u043F\u0435\u0447\u0430\u0442\u044C \u0432 \u0442\u0432\u043E\u0439 \u043A\u043E\u0434 \u0438\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0447\u0435\u0440\u0435\u043F\u0430\u0448\u043A\u0435 \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0432\u044B\u0432\u043E\u0434.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u0427\u0442\u043E-\u0442\u043E \u043F\u043E\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A \u043F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B.\",\n \"Other_error\": \"\u0423\u043F\u0441, \u043A\u0430\u0436\u0435\u0442\u0441\u044F \u0432\u044B\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043E\u0447\u043A\u0430.\",\n \"Program_repair\": \"\u042D\u0442\u043E \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u044B\u0439 \u043A\u043E\u0434, \u043C\u043E\u0436\u0435\u0442\u0435 \u043B\u0438 \u0432\u044B \u0435\u0433\u043E \u0438\u0441\u043F\u0440\u0430\u0432\u0438\u0442\u044C?\",\n \"Program_too_long\": \"\u0412\u0430\u0448\u0430 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043E\u043B\u0433\u043E.\",\n \"ServerError\": \"\u0412\u044B \u043D\u0430\u043F\u0438\u0441\u0430\u043B\u0438 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043C\u044B \u043D\u0435 \u043E\u0436\u0438\u0434\u0430\u043B\u0438. \u0415\u0441\u043B\u0438 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u043E\u043C\u043E\u0447\u044C, \u043F\u0440\u0438\u0448\u043B\u0438\u0442\u0435 \u043D\u0430\u043C \u043F\u0438\u0441\u044C\u043C\u043E \u0441 \u0443\u043A\u0430\u0437\u0430\u043D\u0438\u0435\u043C \u0443\u0440\u043E\u0432\u043D\u044F \u0438 \u0432\u0430\u0448\u0435\u0439 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 hello@hedy.org. \u0410 \u043F\u043E\u043A\u0430 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0447\u0442\u043E-\u0442\u043E \u0434\u0440\u0443\u0433\u043E\u0435 \u0438 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437 \u043D\u0430 \u043F\u0440\u0438\u043C\u0435\u0440\u044B. \u0421\u043F\u0430\u0441\u0438\u0431\u043E!\",\n \"Transpile_error\": \"\u041C\u044B \u043D\u0435 \u043C\u043E\u0436\u0435\u043C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0442\u0432\u043E\u044E \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443.\",\n \"Transpile_success\": \"\u041E\u0442\u043B\u0438\u0447\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430!\\n\u041F\u043E\u0442\u0440\u044F\u0441\u0430\u044E\u0449\u0435!\\n\u041E\u0442\u043B\u0438\u0447\u043D\u043E \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C!\\n\u041F\u0440\u0435\u043A\u0440\u0430\u0441\u043D\u043E!\\n\u0423 \u0442\u0435\u0431\u044F \u043E\u0442\u043B\u0438\u0447\u043D\u043E \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442\u0441\u044F!\",\n \"Transpile_warning\": \"\u0412\u043D\u0438\u043C\u0430\u043D\u0438\u0435!\",\n \"Unsaved_Changes\": \"\u0423 \u0442\u0435\u0431\u044F \u043D\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0430 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430. \u0425\u043E\u0447\u0435\u0448\u044C \u0432\u044B\u0439\u0442\u0438 \u0431\u0435\u0437 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"\u0421\u043A\u043E\u043F\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u0441\u044B\u043B\u043A\u0443, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F\",\n \"customization_deleted\": \"\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u0443\u0434\u0430\u043B\u0435\u043D\u044B.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u041F\u0440\u044F\u043C\u043E\u0435 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0435\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u0423\u0440\u043E\u0432\u0435\u043D\u044C\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"\u0414\u043E\u0431\u0440\u043E \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C \u0432 Hedy! \u0422\u0435\u043F\u0435\u0440\u044C \u0432\u044B \u044F\u0432\u043B\u044F\u0435\u0442\u0435\u0441\u044C \u0433\u043E\u0440\u0434\u044B\u043C \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0435\u043C \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u0443\u0447\u0438\u0442\u0435\u043B\u044F, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0432\u0430\u043C \u0441\u043E\u0437\u0434\u0430\u0432\u0430\u0442\u044C \u043A\u043B\u0430\u0441\u0441\u044B \u0438 \u043F\u0440\u0438\u0433\u043B\u0430\u0448\u0430\u0442\u044C \u0443\u0447\u0435\u043D\u0438\u043A\u043E\u0432.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"\u0415\u0441\u0442\u044C \u043D\u0435\u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F, \u0432\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u043E\u043A\u0438\u043D\u0443\u0442\u044C \u044D\u0442\u0443 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443?\"\n },\n \"sq\": {\n \"CheckInternet\": \"Shikoni n\u00EBse lidhja juaj e internetit po funksionon si\u00E7 duhet.\",\n \"Connection_error\": \"Nuk arrit\u00EBm dot te serveri.\",\n \"Empty_output\": \"Ky kod funksionon, por nuk printon asgj\u00EB. Shtoni komand\u00EBn print n\u00EB kodin tuaj ose p\u00EBrdorni breshk\u00EBn p\u00EBr t\u00EB marr\u00EB rezultatin.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Di\u00E7ka shkoi keq gjat\u00EB ekzekutimit t\u00EB programit.\",\n \"Other_error\": \"Oops! Ndoshta kemi b\u00EBr\u00EB nj\u00EB gabim t\u00EB vog\u00EBl.\",\n \"Program_repair\": \"Ky mund t\u00EB jet\u00EB kodi i duhur, a mund ta rregulloni?\",\n \"Program_too_long\": \"Programi juaj merr shum\u00EB koh\u00EB p\u00EBr t\u00EB ekzekutuar.\",\n \"ServerError\": \"Ke shkruar nj\u00EB program q\u00EB nuk e prisnim. N\u00EBse d\u00EBshironi t\u00EB ndihmoni, na d\u00EBrgoni nj\u00EB email me nivelin dhe programin tuaj n\u00EB hello@hedy.org. Nd\u00EBrkoh\u00EB, provoni di\u00E7ka pak m\u00EB ndryshe dhe hidhini nj\u00EB sy shembujve. Faleminderit!\",\n \"Transpile_error\": \"Ne nuk mund ta ekzekutojm\u00EB programin tuaj.\",\n \"Transpile_success\": \"Pun\u00EB e mir\u00EB!\\nE mahnitshme!\\nTe lumte!\\nE shk\u00EBlqyeshme!\\nJu dol\u00EBt shum\u00EB mir\u00EB!\",\n \"Transpile_warning\": \"Paralajm\u00EBrim!\",\n \"Unsaved_Changes\": \"Ju keni nj\u00EB program t\u00EB paruajtur. D\u00EBshiron t\u00EB largohesh pa e ruajtur?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"sr\": {\n \"CheckInternet\": \"Proveri da li ima\u0161 internet konekciju.\",\n \"Connection_error\": \"Ne mogu dostignuti server.\",\n \"Empty_output\": \"Ovaj kod radi, ali ne prikazuje ni\u0161ta. Dodaj komandu za prikazivanje u kodu ili koristi turtle da prika\u017Ee\u0161 rezultat.\",\n \"Errors_found\": \"Napravio si gre\u0161ku! Ne brini, i dalje smo uspeli da pokrenemo program\",\n \"Execute_error\": \"Ne\u0161to se pokvarilo dok je program radio.\",\n \"Other_error\": \"Ups! Mo\u017Eda smo napravili malu gre\u0161ku.\",\n \"Program_repair\": \"Mo\u017Eda je ovo ispravan kod, mo\u017Ee\u0161 li ga popraviti?\",\n \"Program_too_long\": \"Tvoj program zahteva mnogo vremena za pokretanje.\",\n \"ServerError\": \"Napisali ste program koji nismo o\u010Dekivali. Ako \u017Eelite da pomognete, po\u0161aljite nam mejl sa nivoom i va\u0161im programom na hello@hedy.org. U me\u0111uvremenu, poku\u0161ajte ne\u0161to malo druga\u010Dije i pogledajte jo\u0161 jednom primere. Hvala!\",\n \"Transpile_error\": \"Ne mo\u017Eemo da pokrenemo tvoj program.\",\n \"Transpile_success\": \"Dobar posao!\\nNeverovatno!\\nDobro ura\u0111eno!\\nOdli\u010Dno!\\nBio si odli\u010Dan!\",\n \"Transpile_warning\": \"Upozorenje!\",\n \"Unsaved_Changes\": \"Nisi sa\u010Duvao program. \u017Deli\u0161 li da iza\u0111e\u0161 bez \u010Duvanja?\",\n \"adventures_restored\": \"Podrazumevane avanture su vra\u0107ene.\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Nivo\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"sv\": {\n \"CheckInternet\": \"Kontrollera att din internetanslutning fungerar.\",\n \"Connection_error\": \"Vi kunde inte n\u00E5 servern.\",\n \"Empty_output\": \"Den h\u00E4r koden fungerar men skriver inget. L\u00E4gg till ett utskriftskommando till din kod eller anv\u00E4nd sk\u00F6ldpaddan f\u00F6r att f\u00E5 n\u00E5got resultat.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"N\u00E5got gick fel n\u00E4r programmet k\u00F6rdes.\",\n \"Other_error\": \"Hoppsan! Vi gjorde nog ett litet misstag.\",\n \"Program_repair\": \"Detta kan vara r\u00E4tt kod; kan du laga den?\",\n \"Program_too_long\": \"Ditt program tar f\u00F6r l\u00E5ng tid att k\u00F6ra.\",\n \"ServerError\": \"Du skrev ett program som vi inte v\u00E4ntade oss. Om du vill hj\u00E4lpa oss, skicka ett mejl med niv\u00E5n och ditt program till hello@hedy.org. Under tiden kan du testa att \u00E4ndra lite och ta en ny titt p\u00E5 exemplen. Tack!\",\n \"Transpile_error\": \"Vi kan inte k\u00F6ra ditt program.\",\n \"Transpile_success\": \"Bra jobbat!\\nFantastiskt!\\nBra jobbat!\\nUtm\u00E4rkt!\\nBra jobbat!\",\n \"Transpile_warning\": \"Varning!\",\n \"Unsaved_Changes\": \"Ditt program \u00E4r inte sparat. Vill du l\u00E4mna utan att spara det?\",\n \"adventures_restored\": \"Standard\u00E4ventyren har \u00E5terst\u00E4llts.\",\n \"copy_link_to_share\": \"Kopiera l\u00E4nken f\u00F6r att dela den\",\n \"customization_deleted\": \"Anpassningarna har tagits bort.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u00D6ppna direkt\",\n \"disabled\": \"Inaktiverad\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Niv\u00E5\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"V\u00E4lkommen till Hedy! Du \u00E4r nu den stolta \u00E4garen av ett l\u00E4rarkonto som ger dig m\u00F6jlighet att skapa klasser och bjuda in elever.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Det finns osparade \u00E4ndringar; \u00E4r du s\u00E4ker p\u00E5 att du vill l\u00E4mna sidan?\"\n },\n \"sw\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"Hatukuweza kufikia seva.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Matatizo fulani ilitokea wakati wa kuendesha programu.\",\n \"Other_error\": \"Oops! Labda tulifanya makosa kidogo.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"Hatuwezi kuendesha programu yako.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Onyo!\",\n \"Unsaved_Changes\": \"Una programu ambao haujahifadhiwa. Je! Unataka kuondoka bila kuiokoa?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Kiwango\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"te\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"th\": {\n \"CheckInternet\": \"\u0E40\u0E2D\u0E4B \u0E2D\u0E34\u0E19\u0E40\u0E15\u0E2D\u0E23\u0E4C\u0E40\u0E19\u0E47\u0E15\u0E21\u0E35\u0E1B\u0E31\u0E0D\u0E2B\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E1B\u0E25\u0E48\u0E32\",\n \"Connection_error\": \"\u0E40\u0E23\u0E32\u0E15\u0E34\u0E14\u0E15\u0E48\u0E2D\u0E01\u0E31\u0E1A\u0E40\u0E0B\u0E34\u0E1F\u0E40\u0E27\u0E2D\u0E23\u0E4C\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49.\",\n \"Empty_output\": \"\u0E42\u0E04\u0E49\u0E14\u0E19\u0E35\u0E49\u0E23\u0E31\u0E19\u0E44\u0E14\u0E49\u0E1B\u0E01\u0E15\u0E34\u0E19\u0E30 \u0E41\u0E15\u0E48\u0E27\u0E48\u0E32\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49\u0E1E\u0E34\u0E21\u0E1E\u0E4C\u0E2D\u0E30\u0E44\u0E23\u0E2D\u0E2D\u0E01\u0E21\u0E32\u0E40\u0E25\u0E22 \u0E19\u0E48\u0E32\u0E08\u0E30\u0E25\u0E37\u0E21\u0E43\u0E0A\u0E49\u0E04\u0E33\u0E2A\u0E31\u0E48\u0E07\u0E41\u0E2A\u0E14\u0E07\u0E2B\u0E23\u0E37\u0E2D\u0E43\u0E0A\u0E49\u0E43\u0E2B\u0E49\u0E40\u0E15\u0E48\u0E32\u0E27\u0E32\u0E14\u0E23\u0E39\u0E1B\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E1B\u0E25\u0E48\u0E32\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u0E40\u0E01\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07\u0E23\u0E31\u0E19\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\",\n \"Other_error\": \"\u0E2D\u0E38\u0E49\u0E22\u0E22! \u0E19\u0E48\u0E32\u0E08\u0E30\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14\u0E2D\u0E30\u0E44\u0E23\u0E40\u0E25\u0E47\u0E01\u0E19\u0E49\u0E2D\u0E22\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E1B\u0E25\u0E48\u0E32\",\n \"Program_repair\": \"\u0E42\u0E04\u0E49\u0E14\u0E19\u0E35\u0E49\u0E2D\u0E32\u0E08\u0E08\u0E30\u0E16\u0E39\u0E01\u0E41\u0E25\u0E49\u0E27 \u0E41\u0E15\u0E48\u0E04\u0E38\u0E13\u0E25\u0E2D\u0E07\u0E41\u0E01\u0E49\u0E2B\u0E19\u0E48\u0E2D\u0E22\u0E44\u0E14\u0E49\u0E21\u0E31\u0E49\u0E22?\",\n \"Program_too_long\": \"\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\u0E19\u0E35\u0E49\u0E43\u0E0A\u0E49\u0E40\u0E27\u0E25\u0E32\u0E23\u0E31\u0E19\u0E19\u0E32\u0E19\u0E40\u0E01\u0E34\u0E19\u0E44\u0E1B\u0E19\u0E30\",\n \"ServerError\": \"\u0E04\u0E38\u0E13\u0E40\u0E02\u0E35\u0E22\u0E19\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\u0E17\u0E35\u0E48\u0E40\u0E23\u0E32\u0E44\u0E21\u0E48\u0E04\u0E34\u0E14\u0E27\u0E48\u0E32\u0E08\u0E30\u0E21\u0E35\u0E04\u0E19\u0E40\u0E02\u0E35\u0E22\u0E19\u0E21\u0E32 \u0E40\u0E23\u0E32\u0E08\u0E30\u0E02\u0E2D\u0E1A\u0E04\u0E38\u0E13\u0E21\u0E32\u0E01\u0E16\u0E49\u0E32\u0E04\u0E38\u0E13\u0E0A\u0E48\u0E27\u0E22\u0E2A\u0E48\u0E07\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\u0E41\u0E25\u0E30\u0E40\u0E25\u0E40\u0E27\u0E25\u0E17\u0E35\u0E48\u0E04\u0E38\u0E13\u0E2D\u0E22\u0E39\u0E48\u0E21\u0E32\u0E17\u0E32\u0E07\u0E2D\u0E35\u0E40\u0E21\u0E25 hello@hedy.org \u0E41\u0E25\u0E30\u0E43\u0E19\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07\u0E19\u0E35\u0E49 \u0E25\u0E2D\u0E07\u0E14\u0E39\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D\u0E25\u0E2D\u0E07\u0E40\u0E02\u0E35\u0E22\u0E19\u0E14\u0E49\u0E27\u0E22\u0E27\u0E34\u0E18\u0E35\u0E2D\u0E37\u0E48\u0E19\u0E14\u0E39\u0E01\u0E48\u0E2D\u0E19\u0E19\u0E30 \u0E02\u0E2D\u0E1A\u0E04\u0E38\u0E13\u0E21\u0E32\u0E01!\",\n \"Transpile_error\": \"\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E2A\u0E32\u0E21\u0E32\u0E23\u0E16\u0E23\u0E31\u0E19\u0E44\u0E14\u0E49\",\n \"Transpile_success\": \"\u0E40\u0E01\u0E48\u0E07\u0E21\u0E32\u0E01!\\n\u0E2A\u0E38\u0E14\u0E22\u0E2D\u0E14!\\n\u0E22\u0E2D\u0E14\u0E40\u0E22\u0E35\u0E48\u0E22\u0E21!\\n\u0E40\u0E25\u0E34\u0E28\u0E21\u0E32\u0E01!\\n\u0E17\u0E33\u0E44\u0E14\u0E49\u0E14\u0E35\u0E21\u0E32\u0E01!\",\n \"Transpile_warning\": \"\u0E04\u0E33\u0E40\u0E15\u0E37\u0E2D\u0E19!\",\n \"Unsaved_Changes\": \"\u0E42\u0E1B\u0E23\u0E41\u0E01\u0E23\u0E21\u0E17\u0E35\u0E48\u0E40\u0E02\u0E35\u0E22\u0E19\u0E22\u0E31\u0E07\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49\u0E40\u0E0B\u0E1F\u0E19\u0E30 \u0E08\u0E30\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E2B\u0E19\u0E49\u0E32\u0E19\u0E35\u0E49\u0E40\u0E25\u0E22 \u0E42\u0E14\u0E22\u0E44\u0E21\u0E48\u0E40\u0E0B\u0E1F\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E23\u0E2D?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"tl\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"tn\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"tr\": {\n \"CheckInternet\": \"\u0130nternet ba\u011Flant\u0131n\u0131z do\u011Fru \u00E7al\u0131\u015F\u0131yor mu diye bir g\u00F6z at\u0131n.\",\n \"Connection_error\": \"Sunucuya ula\u015Famad\u0131k.\",\n \"Empty_output\": \"Bu kod \u00E7al\u0131\u015F\u0131yor ama hi\u00E7bir \u015Fey yazd\u0131rm\u0131yor. Kodunuzda bir yazd\u0131rma komutu kullan\u0131n veya bir \u00E7\u0131kt\u0131 almak i\u00E7in kaplumba\u011Fay\u0131 kullan\u0131n.\",\n \"Errors_found\": \"Bir hata yapt\u0131n\u0131z! Endi\u015Felenmeyin, program\u0131 yine de \u00E7al\u0131\u015Ft\u0131rd\u0131k\",\n \"Execute_error\": \"Program\u0131 \u00E7al\u0131\u015Ft\u0131r\u0131rken bir \u015Feyler ters gitti.\",\n \"Other_error\": \"Oops! Belki k\u00FC\u00E7\u00FCk bir hata yapm\u0131\u015F olabiliriz.\",\n \"Program_repair\": \"Bu do\u011Fru kod olabilirdi, onu d\u00FCzeltebilir misin?\",\n \"Program_too_long\": \"Program\u0131n\u0131z\u0131n \u00E7al\u0131\u015Fmas\u0131 \u00E7ok uzun s\u00FCr\u00FCyor.\",\n \"ServerError\": \"Bizim beklemedi\u011Fimiz bir program yazd\u0131n\u0131z. E\u011Fer yard\u0131m etmek isterseniz, bize hello@hedy.org adresinden ilgili seviye ve program\u0131n\u0131zla birlikte bir e-posta g\u00F6nderin. Bu arada, biraz farkl\u0131 bir \u015Fey deneyin ve \u00F6rneklere bir kez daha g\u00F6z at\u0131n. Te\u015Fekk\u00FCrler!\",\n \"Transpile_error\": \"Program\u0131n\u0131 \u00E7al\u0131\u015Ft\u0131ram\u0131yoruz.\",\n \"Transpile_success\": \"Aferin!\\n\u0130nan\u0131lmaz!\\n\u0130yi i\u015Fti, Aferin!\\nM\u00FCkemmel!\\n\u00C7ok iyiydin!\",\n \"Transpile_warning\": \"Uyar\u0131!\",\n \"Unsaved_Changes\": \"Kaydedilmemi\u015F bir program\u0131n var. Kaydetmeden ayr\u0131lmak m\u0131 istiyorsun?\",\n \"adventures_restored\": \"Varsay\u0131lan maceralar geri y\u00FCklendi.\",\n \"copy_link_to_share\": \"Payla\u015Fmak i\u00E7in ba\u011Flant\u0131y\u0131 kopyalay\u0131n\",\n \"customization_deleted\": \"\u00D6zelle\u015Ftirmeler ba\u015Far\u0131yla silindi.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Do\u011Frudan a\u00E7\u0131n\",\n \"disabled\": \"Devre d\u0131\u015F\u0131\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Seviye\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Hedy'ye ho\u015F geldiniz! Art\u0131k s\u0131n\u0131flar olu\u015Fturman\u0131za ve \u00F6\u011Frencileri davet etmenize olanak tan\u0131yan bir \u00F6\u011Fretmen hesab\u0131n\u0131n gururlu sahibisiniz.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"Kaydedilmemi\u015F de\u011Fi\u015Fiklikler var, bu sayfadan ayr\u0131lmak istedi\u011Finize emin misiniz?\"\n },\n \"uk\": {\n \"CheckInternet\": \"\u041F\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435, \u0447\u0438 \u0432\u0430\u0448\u0435 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F \u0434\u043E \u0406\u043D\u0442\u0435\u0440\u043D\u0435\u0442\u0443 \u043F\u0440\u0430\u0446\u044E\u0454 \u043D\u0430\u043B\u0435\u0436\u043D\u0438\u043C \u0447\u0438\u043D\u043E\u043C.\",\n \"Connection_error\": \"\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u0438\u0441\u044F \u0434\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0430.\",\n \"Empty_output\": \"\u0426\u0435\u0439 \u043A\u043E\u0434 \u043F\u0440\u0430\u0446\u044E\u0454, \u0430\u043B\u0435 \u043D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0434\u0440\u0443\u043A\u0443\u0454. \u0414\u043E\u0434\u0430\u0439\u0442\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u0434\u0440\u0443\u043A\u0443 \u0434\u043E \u0441\u0432\u043E\u0433\u043E \u043A\u043E\u0434\u0443 \u0430\u0431\u043E \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u0435\u043F\u0430\u0445\u0443, \u0449\u043E\u0431 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0432\u0438\u0432\u0456\u0434.\",\n \"Errors_found\": \"\u0412\u0438 \u0437\u0440\u043E\u0431\u0438\u043B\u0438 \u043F\u043E\u043C\u0438\u043B\u043A\u0443! \u041D\u0435 \u0442\u0443\u0440\u0431\u0443\u0439\u0442\u0435\u0441\u044F, \u043C\u0438 \u043F\u0440\u043E\u0434\u043E\u0432\u0438\u043C\u043E \u0432\u0438\u043A\u043E\u043D\u0443\u0432\u0430\u0442\u0438 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0443\",\n \"Execute_error\": \"\u0429\u043E\u0441\u044C \u043F\u0456\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A \u043F\u0456\u0434 \u0447\u0430\u0441 \u0437\u0430\u043F\u0443\u0441\u043A\u0443 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0438.\",\n \"Other_error\": \"\u041E\u0439! \u041C\u043E\u0436\u043B\u0438\u0432\u043E, \u043C\u0438 \u0442\u0440\u043E\u0445\u0438 \u043F\u043E\u043C\u0438\u043B\u0438\u043B\u0438\u0441\u044F.\",\n \"Program_repair\": \"\u0426\u0435 \u043C\u043E\u0436\u0435 \u0431\u0443\u0442\u0438 \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u043A\u043E\u0434, \u0432\u0438 \u043C\u043E\u0436\u0435\u0442\u0435 \u0439\u043E\u0433\u043E \u0432\u0438\u043F\u0440\u0430\u0432\u0438\u0442\u0438?\",\n \"Program_too_long\": \"\u0412\u0430\u0448\u0430 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0430 \u0432\u0438\u043A\u043E\u043D\u0443\u0454\u0442\u044C\u0441\u044F \u043D\u0430\u0434\u0442\u043E \u0434\u043E\u0432\u0433\u043E.\",\n \"ServerError\": \"\u0412\u0438 \u043D\u0430\u043F\u0438\u0441\u0430\u043B\u0438 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0443, \u044F\u043A\u0443 \u043C\u0438 \u043D\u0435 \u043E\u0447\u0456\u043A\u0443\u0432\u0430\u043B\u0438. \u042F\u043A\u0449\u043E \u0432\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u0434\u043E\u043F\u043E\u043C\u043E\u0433\u0442\u0438, \u043D\u0430\u0434\u0456\u0448\u043B\u0456\u0442\u044C \u043D\u0430\u043C \u0435\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u0438\u0439 \u043B\u0438\u0441\u0442 \u0456\u0437 \u0437\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F\u043C \u0440\u0456\u0432\u043D\u044F \u0442\u0430 \u0432\u0430\u0448\u043E\u0457 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0438 \u043D\u0430 hello@hedy.org. \u0422\u0438\u043C \u0447\u0430\u0441\u043E\u043C \u0441\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0449\u043E\u0441\u044C \u0442\u0440\u043E\u0445\u0438 \u0456\u043D\u0448\u0435 \u0442\u0430 \u0449\u0435 \u0440\u0430\u0437 \u043F\u043E\u0434\u0438\u0432\u0456\u0442\u044C\u0441\u044F \u043D\u0430 \u043F\u0440\u0438\u043A\u043B\u0430\u0434\u0438. \u0414\u044F\u043A\u0443\u044E!\",\n \"Transpile_error\": \"\u041C\u0438 \u043D\u0435 \u043C\u043E\u0436\u0435\u043C\u043E \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438 \u0432\u0430\u0448\u0443 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0443.\",\n \"Transpile_success\": \"\u0413\u0430\u0440\u043D\u0430 \u0440\u043E\u0431\u043E\u0442\u0430!\\n\u0414\u0438\u0432\u043E\u0432\u0438\u0436\u043D\u043E!\\n\u0427\u0443\u0434\u043E\u0432\u0430 \u0440\u043E\u0431\u043E\u0442\u0430!\\n\u0427\u0443\u0434\u043E\u0432\u043E!\\n\u0412\u0438 \u0447\u0443\u0434\u043E\u0432\u043E \u0432\u043F\u043E\u0440\u0430\u043B\u0438\u0441\u044F!\",\n \"Transpile_warning\": \"\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u0436\u0435\u043D\u043D\u044F!\",\n \"Unsaved_Changes\": \"\u0412\u0438 \u043C\u0430\u0454\u0442\u0435 \u043D\u0435\u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u0443 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u0443. \u0412\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u0432\u0438\u0439\u0442\u0438, \u043D\u0435 \u0437\u0431\u0435\u0440\u0456\u0433\u0448\u0438 \u0457\u0457?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"\u0421\u043A\u043E\u043F\u0456\u044E\u0439\u0442\u0435 \u043F\u043E\u0441\u0438\u043B\u0430\u043D\u043D\u044F, \u0449\u043E\u0431 \u043F\u043E\u0434\u0456\u043B\u0438\u0442\u0438\u0441\u044F\",\n \"customization_deleted\": \"\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F \u0443\u0441\u043F\u0456\u0448\u043D\u043E \u0432\u0438\u0434\u0430\u043B\u0435\u043D\u043E.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u0411\u0435\u0437\u043F\u043E\u0441\u0435\u0440\u0435\u0434\u043D\u044C\u043E \u0432\u0456\u0434\u043A\u0440\u0438\u0442\u0438\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u0420\u0456\u0432\u0435\u043D\u044C\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"ur\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"vi\": {\n \"CheckInternet\": \"H\u00E3y xem k\u1EBFt n\u1ED1i Internet c\u1EE7a b\u1EA1n c\u00F3 ho\u1EA1t \u0111\u1ED9ng b\u00ECnh th\u01B0\u1EDDng kh\u00F4ng.\",\n \"Connection_error\": \"Ch\u00FAng t\u00F4i kh\u00F4ng th\u1EC3 k\u1EBFt n\u1ED1i t\u1EDBi server.\",\n \"Empty_output\": \"Ch\u01B0\u01A1ng tr\u00ECnh n\u00E0y ho\u1EA1t \u0111\u1ED9ng nh\u01B0ng kh\u00F4ng in ra g\u00EC c\u1EA3. Th\u00EAm l\u1EC7nh in v\u00E0o ch\u01B0\u01A1ng tr\u00ECnh ho\u1EB7c s\u1EED d\u1EE5ng con r\u00F9a \u0111\u1EC3 c\u00F3 \u0111\u1EA7u ra nh\u00E9.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"\u0110\u00E3 x\u1EA3y ra s\u1EF1 c\u1ED1 khi ch\u1EA1y ch\u01B0\u01A1ng tr\u00ECnh.\",\n \"Other_error\": \"R\u1EA5t ti\u1EBFc! C\u00F3 l\u1EBD ch\u00FAng t\u00F4i \u0111\u00E3 ph\u1EA1m m\u1ED9t sai l\u1EA7m nh\u1ECF.\",\n \"Program_repair\": \"\u0110\u00E2y c\u00F3 th\u1EC3 l\u00E0 ch\u01B0\u01A1ng tr\u00ECnh ch\u00EDnh x\u00E1c, b\u1EA1n c\u00F3 th\u1EC3 s\u1EEDa n\u00F3 kh\u00F4ng?\",\n \"Program_too_long\": \"Ch\u01B0\u01A1ng tr\u00ECnh c\u1EE7a b\u1EA1n t\u1ED1n qu\u00E1 nhi\u1EC1u th\u1EDDi gian \u0111\u1EC3 ch\u1EA1y.\",\n \"ServerError\": \"B\u1EA1n \u0111\u00E3 vi\u1EBFt m\u1ED9t ch\u01B0\u01A1ng tr\u00ECnh m\u00E0 ch\u00FAng t\u00F4i kh\u00F4ng mong \u0111\u1EE3i. N\u1EBFu b\u1EA1n mu\u1ED1n tr\u1EE3 gi\u00FAp, h\u00E3y g\u1EEDi email cho ch\u00FAng t\u00F4i v\u1EC1 c\u1EA5p \u0111\u1ED9 v\u00E0 ch\u01B0\u01A1ng tr\u00ECnh c\u1EE7a b\u1EA1n t\u1EA1i hello@hedy.org. Trong th\u1EDDi gian ch\u1EDD \u0111\u1EE3i, h\u00E3y th\u1EED l\u00E0m g\u00EC \u0111\u00F3 kh\u00E1c bi\u1EC7t m\u1ED9t ch\u00FAt v\u00E0 xem l\u1EA1i c\u00E1c v\u00ED d\u1EE5. Xin c\u1EA3m \u01A1n!\",\n \"Transpile_error\": \"Ch\u00FAng t\u00F4i kh\u00F4ng th\u1EC3 ch\u1EA1y ch\u01B0\u01A1ng tr\u00ECnh c\u1EE7a b\u1EA1n.\",\n \"Transpile_success\": \"L\u00E0m t\u1ED1t l\u1EAFm!\\nR\u1EA5t tuy\u1EC7t v\u1EDDi!\\nT\u1ED1t l\u1EAFm!\\nHay qu\u00E1!\\nB\u1EA1n l\u00E0m r\u1EA5t t\u1ED1t!\",\n \"Transpile_warning\": \"C\u1EA3nh b\u00E1o!\",\n \"Unsaved_Changes\": \"B\u1EA1n c\u00F3 m\u1ED9t ch\u01B0\u01A1ng tr\u00ECnh ch\u01B0a \u0111\u01B0\u1EE3c l\u01B0u. B\u1EA1n c\u00F3 mu\u1ED1n tho\u00E1t m\u00E0 kh\u00F4ng l\u01B0u n\u00F3 kh\u00F4ng?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDD28,\uD83D\uDCDC\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"zh_Hans\": {\n \"CheckInternet\": \"\u67E5\u770B\u4E00\u4E0B\u4F60\u7684\u7F51\u7EDC\u8FDE\u63A5\u662F\u5426\u6B63\u5E38.\",\n \"Connection_error\": \"\u6211\u4EEC\u65E0\u6CD5\u8FDE\u63A5\u5230\u670D\u52A1\u5668\u3002\",\n \"Empty_output\": \"\u8FD9\u6BB5\u4EE3\u7801\u53EF\u4EE5\u5DE5\u4F5C\uFF0C\u4F46\u6CA1\u6709\u6253\u5370\u4EFB\u4F55\u4E1C\u897F\u3002\u5728\u4F60\u7684\u4EE3\u7801\u4E2D\u6DFB\u52A0\u4E00\u4E2A\u6253\u5370\u547D\u4EE4\uFF0C\u6216\u8005\u4F7F\u7528\u4E4C\u9F9F\u6765\u83B7\u5F97\u8F93\u51FA\u3002\",\n \"Errors_found\": \"\u4F60\u641E\u9519\u4E86\uFF01\u4E0D\u8981\u7740\u6025\uFF0C\u6211\u4EEC\u4ECD\u7136\u8FD0\u884C\u8BE5\u7A0B\u5E8F\",\n \"Execute_error\": \"\u5728\u8FD0\u884C\u7A0B\u5E8F\u65F6\u51FA\u4E86\u70B9\u95EE\u9898\u3002\",\n \"Other_error\": \"\u7CDF\u7CD5\uFF01 \u4E5F\u8BB8\u6211\u4EEC\u72AF\u4E86\u4E00\u4E2A\u5C0F\u9519\u8BEF\u3002\",\n \"Program_repair\": \"\u8FD9\u53EF\u80FD\u662F\u6B63\u786E\u7684\u4EE3\u7801\uFF0C\u4F60\u80FD\u4FEE\u590D\u5B83\u5417\uFF1F\",\n \"Program_too_long\": \"\u4F60\u7684\u7A0B\u5E8F\u8FD0\u884C\u7684\u65F6\u95F4\u592A\u957F\u4E86\u3002\",\n \"ServerError\": \"\u4F60\u5199\u4E86\u4E00\u4E2A\u51FA\u4E4E\u6211\u4EEC\u610F\u6599\u7684\u7A0B\u5E8F\u3002\u5982\u679C\u60A8\u60F3\u63D0\u4F9B\u5E2E\u52A9\uFF0C\u8BF7\u901A\u8FC7 hello@hedy.org \u5411\u6211\u4EEC\u53D1\u9001\u4E00\u5C01\u7535\u5B50\u90AE\u4EF6\uFF0C\u5176\u4E2D\u5305\u542B\u7EA7\u522B\u548C\u60A8\u7684\u7A0B\u5E8F\u3002\u4E0E\u6B64\u540C\u65F6\uFF0C\u5C1D\u8BD5\u4E00\u4E9B\u4E0D\u540C\u7684\u4E1C\u897F\uFF0C\u518D\u770B\u770B\u8FD9\u4E9B\u4F8B\u5B50\u3002\u8C22\u8C22\uFF01\",\n \"Transpile_error\": \"\u6211\u4EEC\u65E0\u6CD5\u8FD0\u884C\u4F60\u7684\u7A0B\u5E8F\u3002\",\n \"Transpile_success\": \"\u597D\u6837\u7684\uFF01\\n\u4E86\u4E0D\u8D77\uFF01\\n\u505A\u7684\u5F88\u597D\uFF01\\n\u68D2\u6781\u4E86\uFF01\\n\u505A\u7684\u771F\u68D2\uFF01\",\n \"Transpile_warning\": \"\u8B66\u544A\uFF01\",\n \"Unsaved_Changes\": \"\u4F60\u6709\u4E00\u4E2A\u672A\u4FDD\u5B58\u7684\u7A0B\u5E8F\u3002\u4F60\u60F3\u4E0D\u4FDD\u5B58\u5C31\u79BB\u5F00\u5417\uFF1F\",\n \"adventures_restored\": \"\u9ED8\u8BA4\u7684\u5192\u9669\u5DF2\u7ECF\u6062\u590D\uFF01\",\n \"copy_link_to_share\": \"\u590D\u5236\u94FE\u63A5\u4EE5\u5206\u4EAB\",\n \"customization_deleted\": \"\u5DF2\u6210\u529F\u5220\u9664\u81EA\u5B9A\u4E49\u9879\u3002\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"\u76F4\u63A5\u6253\u5F00\",\n \"disabled\": \"\u7981\u7528\u7684\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"\u7EA7\u522B\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n },\n \"zh_Hant\": {\n \"CheckInternet\": \"Check whether your Internet connection is working.\",\n \"Connection_error\": \"We couldn't reach the server.\",\n \"Empty_output\": \"This code works but does not print anything. Add a print command to your code or use the turtle to get output.\",\n \"Errors_found\": \"You made a mistake! Don't worry, Hedy is trying to find the mistakes\",\n \"Execute_error\": \"Something went wrong while running the program.\",\n \"Other_error\": \"Oops! Maybe we made a little mistake.\",\n \"Program_repair\": \"This could be the correct code, can you fix it?\",\n \"Program_too_long\": \"Your program takes too long to run.\",\n \"ServerError\": \"You wrote a program we weren't expecting. If you want to help, send us an email with the level and your program at hello@hedy.org. In the meantime, try something a little different and take another look at the examples. Thanks!\",\n \"Transpile_error\": \"We can't run your program.\",\n \"Transpile_success\": \"Good job!\\nAmazing!\\nWell done!\\nExcellent!\\nYou did great!\",\n \"Transpile_warning\": \"Warning!\",\n \"Unsaved_Changes\": \"You have an unsaved program. Do you want to leave without saving it?\",\n \"adventures_restored\": \"The default adventures have been restored!\",\n \"copy_link_to_share\": \"Copy link to share\",\n \"customization_deleted\": \"Customizations successfully deleted.\",\n \"dice\": \"\uD83C\uDFB2\",\n \"directly_available\": \"Directly open\",\n \"disabled\": \"Disabled\",\n \"fortune\": \"\uD83D\uDD2E, \u2728\",\n \"haunted\": \"\uD83E\uDD87, \uD83D\uDC7B, \uD83C\uDF83\",\n \"level_title\": \"Level\",\n \"restaurant\": \"\uD83C\uDF63, \uD83C\uDF55, \uD83C\uDF54\",\n \"rock\": \"\u2702\uFE0F, \uD83D\uDCDC, \uD83D\uDDFB\",\n \"songs\": \"\uD83C\uDFB5,\uD83C\uDFB6\",\n \"teacher_welcome\": \"Welcome to Hedy! Your are now the proud owner of a teachers account which allows you to create classes and invite students.\",\n \"turtle\": \"\uD83D\uDC22\",\n \"unsaved_class_changes\": \"There are unsaved changes, are you sure you want to leave this page?\"\n }\n};\n", "\nimport { MessageKey, TRANSLATIONS } from './message-translations';\n\nexport let ClientMessages: Record = Object.assign({}, TRANSLATIONS['en']);\n\n/**\n * Switch the values in the 'ErrorMessages' global\n */\nexport function setClientMessageLanguage(key: string) {\n // Mutate the object in-place, so that all imported references are still valid\n Object.assign(ClientMessages, TRANSLATIONS[key] ?? TRANSLATIONS['en']);\n}\n", "/**\n * The modal we pop up to have children confirm things\n */\n\nclass Modal {\n constructor() {\n // Just one binding, never needs stat\n $('#modal-confirm-button').on('click', () => this.hide());\n $('#modal-no-button').on('click', () => this.hide());\n $('#modal-cancel-button').on('click', () => this.hide());\n $('#modal-copy-ok-button').on('click', () => this.hide());\n $('#modal-copy-close-button').on('click', () => this.hide());\n $('#modal-repair-button').on('click', () => this.hide());\n $('#modal-preview-button').on('click', () => this.hide());\n $('#modal-alert-button').on('click', () => this.hide_alert());\n }\n\n private _timeout?: ReturnType\n private _alert_timeout?: ReturnType\n\n public show() {\n $('#modal-mask').show();\n $('#modal-content').show();\n window.scrollTo(0, 0);\n }\n\n public hide() {\n $('#modal-mask').hide();\n $('#modal-content').hide();\n $('#modal-prompt').hide();\n $('#modal-confirm').hide();\n $('#modal-copy').hide();\n $('#modal-repair').hide();\n $('#modal-preview').hide();\n }\n\n public hide_alert() {\n $('#modal-alert').fadeOut(500);\n }\n\n /**\n * Display a temporary success popup\n */\n public notifySuccess(message: string, timeoutMs: number = 3000) {\n return this.alert(message, timeoutMs);\n }\n\n /**\n * Display a temporary error popup\n */\n public notifyError(message: string, timeoutMs: number = 5000) {\n return this.alert(message, timeoutMs, true);\n }\n\n /**\n * Display a temporary popup\n */\n private alert(message: string, timeoutMs?: number, error?: boolean) {\n $('#modal_alert_container').toggleClass('bg-red-100 border-red-400 text-red-700', !!error);\n $('#modal_alert_container').toggleClass('bg-green-100 border-green-400 text-green-700', !error);\n $('#modal-alert-button').toggleClass('text-red-500', !!error);\n $('#modal-alert-button').toggleClass('text-green-500', !error);\n\n $('#modal_alert_text').html(message);\n $('#modal-alert').fadeIn(500);\n\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._alert_timeout) {\n clearTimeout(this._alert_timeout);\n this._alert_timeout = undefined;\n }\n if (timeoutMs) this._alert_timeout = setTimeout(() => this.hide_alert(), timeoutMs);\n }\n\n public copy_alert(message: string, timeoutMs?: number, title: string = '',) {\n this.hide();\n if(title != '') {\n $('#modal-copy-title').html(title);\n $('#modal-copy-title').removeClass('hidden');\n }\n else{\n $('#modal-copy-title').html('');\n $('#modal-copy-title').addClass('hidden');\n }\n $('#modal-copy-text').html(message);\n this.show();\n $('#modal-copy').show();\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._timeout) {\n clearTimeout(this._timeout);\n this._timeout = undefined;\n }\n if (timeoutMs) this._timeout = setTimeout(() => this.hide(), timeoutMs);\n }\n\n public preview(content: JQuery, title: string) {\n this.hide();\n $('#modal-preview-title').html(title);\n const target = $('#modal-preview-content');\n content.attr('id', 'modal-preview-content');\n target.replaceWith(content);\n\n this.show();\n $('#modal-preview').show();\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._timeout) {\n clearTimeout(this._timeout);\n this._timeout = undefined;\n }\n }\n\n public repair(message: string, timeoutMs?: number, title: string = '') {\n this.hide();\n if(title != '') {\n $('#modal-repair-title').html(title);\n $('#modal-repair-title').removeClass('hidden');\n }\n else{\n $('#modal-repair-title').html('');\n $('#modal-repair-title').addClass('hidden');\n }\n $('#modal-repair-text').html(message);\n this.show();\n $('#modal-repair').show();\n if (timeoutMs) setTimeout(() => this.hide(), timeoutMs);\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._timeout) {\n clearTimeout(this._timeout);\n this._timeout = undefined;\n }\n if (timeoutMs) this._timeout = setTimeout(() => this.hide(), timeoutMs);\n }\n\n /**\n * modal.confirm as a promise\n */\n public confirmP(message: string): Promise {\n return new Promise(ok => this.confirm(message, ok));\n }\n\n // The declineCb is optional, mainly for relic code support: add if needed otherwise leave empty on call\n public confirm(message: string, confirmCb: () => void, declineCb: () => void = function(){}) {\n this.hide();\n $('#modal-confirm-text').text(message);\n this.show();\n $('#modal-confirm').show();\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._timeout) {\n clearTimeout(this._timeout);\n this._timeout = undefined;\n }\n // Since we need to close over the callback, replace the handler\n $('#modal-yes-button').off('click').on('click', () => {\n this.hide();\n confirmCb();\n });\n $('#modal-no-button').off('click').on('click', () => {\n this.hide();\n declineCb();\n });\n }\n\n public prompt(message: string, defaultValue: string, confirmCb: (x: string) => void) {\n this.hide();\n $('#modal-prompt-text').text(message);\n this.show();\n $('#modal-prompt').show();\n if (defaultValue) $('#modal-prompt-input').val(defaultValue);\n // If there's a timeout from a previous modal that hasn't been cleared yet, clear it to avoid hiding the present message before its due time.\n if(this._timeout) {\n clearTimeout(this._timeout);\n this._timeout = undefined;\n }\n\n // Since we need to close over the callback, replace the handler\n $('#modal-ok-button').off('click').on('click', () => {\n this.hide();\n\n const value = $('#modal-prompt-input').val();\n if (typeof value === 'string') {\n // Always empty the value on success -> otherwise this value is shown on new prompt (without a page reload)\n $('#modal-prompt-input').val('');\n confirmCb(value);\n }\n });\n }\n}\n\n/**\n * The error that appears underneath the code editor\n */\nexport const success = {\n hide: function () {\n $('#okbox').hide();\n },\n\n showWarning(caption: string, message: string) {\n $('#okbox .caption').text(caption);\n $('#okbox .details').text(message);\n $('#okbox').show();\n },\n\n show(caption: string) {\n $('#okbox .caption').text(caption);\n $('#okbox').show();\n setTimeout(function() {\n $('#okbox').hide();\n }, 3000);\n }\n}\n\nexport const error = {\n hide() {\n $('#errorbox').hide();\n $('#warningbox').hide();\n },\n showWarning(caption: string, message: string) {\n this.hide();\n $('#warningbox .caption').text(caption);\n $('#warningbox .details').text(message);\n $('#warningbox').show();\n },\n\n show(caption: string, message: string) {\n $('#errorbox .caption').text(caption);\n $('#errorbox .details').html(message);\n $('#errorbox').show();\n },\n\n showFadingWarning(caption: string, message: string) {\n error.showWarning(caption, message);\n setTimeout(function(){\n $('#warningbox').fadeOut();\n }, 10000);\n }\n}\n\nexport const modal = new Modal();\n\n/**\n * Run a code block, show a popup if we catch an exception\n */\nexport async function tryCatchPopup(cb: () => void | Promise) {\n try {\n return await cb();\n } catch (e: any) {\n console.log('Error', e);\n modal.notifyError(e.message);\n }\n}\n", "import { initializeSyntaxHighlighter } from './syntaxModesRules';\nimport { ClientMessages } from './client-messages';\nimport { modal, error, success, tryCatchPopup } from './modal';\nimport JSZip from \"jszip\";\nimport * as Tone from 'tone'\nimport { Tabs } from './tabs';\nimport { MessageKey } from './message-translations';\nimport { turtle_prefix, pygame_prefix, normal_prefix, music_prefix } from './pythonPrefixes'\nimport { Achievement, Adventure, isServerSaveInfo, ServerSaveInfo } from './types';\nimport { startIntroTutorial } from './tutorials/tutorial';\nimport { get_parsons_code, initializeParsons, loadParsonsExercise } from './parsons';\nimport { checkNow, onElementBecomesVisible } from './browser-helpers/on-element-becomes-visible';\nimport { incrementDebugLine, initializeDebugger, load_variables, startDebug } from './debugging';\nimport { localDelete, localLoad, localSave } from './local';\nimport { initializeLoginLinks } from './auth';\nimport { postJson } from './comm';\nimport { LocalSaveWarning } from './local-save-warning';\nimport { HedyEditor, EditorType } from './editor';\nimport { stopDebug } from \"./debugging\";\nimport { HedyCodeMirrorEditorCreator } from './cm-editor';\nimport { initializeTranslation } from './lezer-parsers/tokens';\n\nexport let theGlobalDebugger: any;\nexport let theGlobalEditor: HedyEditor;\nexport let theModalEditor: HedyEditor;\nexport let theGlobalSourcemap: { [x: string]: any; };\nexport const theLocalSaveWarning = new LocalSaveWarning();\nconst editorCreator: HedyCodeMirrorEditorCreator = new HedyCodeMirrorEditorCreator();\nlet last_code: string;\n\n/**\n * Used to record and undo pygame-related settings\n */\nlet pygameRunning = false;\n/**\n * Represents whether there's an open 'ask' prompt\n */\nlet askPromptOpen = false;\n\n// Many bits of code all over this file need this information globally.\n// Not great but it'll do for now until we refactor this file some more.\nlet theAdventures: Record = {};\nexport let theLevel: number = 0;\nexport let theLanguage: string = '';\nexport let theKeywordLanguage: string = 'en';\nlet theStaticRoot: string = '';\nlet currentTab: string;\nlet theUserIsLoggedIn: boolean;\n//create a synth and connect it to the main output (your speakers)\n//const synth = new Tone.Synth().toDestination();\n\nconst synth = new Tone.PolySynth(Tone.Synth).toDestination();\n\n\nconst pygame_suffix =\n`# coding=utf8\npygame_end = True\npygame.quit()\n`;\n\nconst slides_template = `\n\n\n\n\n\t \n\t \n\t \n\tSlides Level - 1 \n\t \n\n\n\n\t\n\t\t
\n\t\t\t{replace}\n\t\t
\n\t
\n\n\t\n\t\n\n\n`;\n\nexport interface InitializeAppOptions {\n readonly level: number;\n readonly keywordLanguage: string;\n /**\n * The URL root where static content is hosted\n */\n readonly staticRoot?: string;\n}\n\n/**\n * Initialize \"global\" parts of the main app\n */\nexport function initializeApp(options: InitializeAppOptions) {\n theLevel = options.level;\n theKeywordLanguage = options.keywordLanguage;\n theStaticRoot = options.staticRoot ?? '';\n // When we are in Alpha or in dev the static root already points to an internal directory\n theStaticRoot = theStaticRoot === '/' ? '' : theStaticRoot;\n initializeSyntaxHighlighter({\n keywordLanguage: options.keywordLanguage,\n });\n initializeCopyToClipboard();\n\n // Close the dropdown menu if the user clicks outside of it\n $(document).on(\"click\", function(event){\n // The following is not needed anymore, but it saves the next for loop if the click is not for dropdown.\n if (!$(event.target).closest(\".dropdown\").length) {\n $(\".dropdown-menu\").slideUp(\"medium\");\n $(\".cheatsheet-menu\").slideUp(\"medium\");\n return;\n }\n\n const allDropdowns = $('.dropdown-menu')\n for (const dd of allDropdowns) {\n // find the closest dropdown button (element) that initiated the event\n const c = $(dd).closest('.dropdown')[0]\n // if the click event target is not within or close to the container, slide up the dropdown menu\n if (!$(event.target).closest(c).length) {\n $(dd).slideUp('fast');\n }\n }\n });\n\n $(\"#search_language\").on('keyup', function() {\n let search_query = ($(\"#search_language\").val() as string).toLowerCase();\n $(\".language\").each(function(){\n if ($(this).html().toLowerCase().includes(search_query)) {\n $(this).show();\n } else {\n $(this).hide();\n }\n });\n });\n\n // All input elements with data-autosubmit=\"true\" automatically submit their enclosing form\n $('*[data-autosubmit=\"true\"]').on('change', (ev) => {\n $(ev.target).closest('form').trigger('submit');\n });\n\n initializeLoginLinks();\n}\n\nexport interface InitializeCodePageOptions {\n readonly page: 'code';\n readonly level: number;\n readonly lang: string;\n readonly adventures: Adventure[];\n readonly start_tutorial?: boolean;\n readonly initial_tab: string;\n readonly current_user_name?: string;\n readonly suppress_save_and_load_for_slides?: boolean;\n}\n\n/**\n * Initialize the actual code page\n */\nexport function initializeCodePage(options: InitializeCodePageOptions) {\n theUserIsLoggedIn = !!options.current_user_name;\n if (theUserIsLoggedIn) {\n theLocalSaveWarning.setLoggedIn();\n }\n\n theAdventures = Object.fromEntries((options.adventures ?? []).map(a => [a.short_name, a]));\n\n // theLevel will already have been set during initializeApp\n if (theLevel != options.level) {\n throw new Error(`initializeApp set level to ${JSON.stringify(theLevel)} but initializeCodePage sets it to ${JSON.stringify(options.level)}`);\n }\n theLanguage = options.lang;\n\n // *** EDITOR SETUP ***\n const $editor = $('#editor');\n if ($editor.length) {\n const dir = $(\"body\").attr(\"dir\");\n theGlobalEditor = editorCreator.initializeEditorWithGutter($editor, EditorType.MAIN, dir);\n initializeTranslation({keywordLanguage: theKeywordLanguage, level: theLevel});\n attachMainEditorEvents(theGlobalEditor);\n initializeDebugger({\n editor: theGlobalEditor,\n level: theLevel,\n language: theLanguage,\n keywordLanguage: theKeywordLanguage,\n });\n }\n\n const anchor = window.location.hash.substring(1);\n\n const validAnchor = [...Object.keys(theAdventures), 'parsons', 'quiz'].includes(anchor) ? anchor : undefined;\n\n const tabs = new Tabs({\n // If we're opening an adventure from the beginning (either through a link to /hedy/adventures or through a saved program for an adventure), we click on the relevant tab.\n // We click on `level` to load a program associated with level, if any.\n initialTab: validAnchor ?? options.initial_tab,\n });\n\n tabs.on('beforeSwitch', () => {\n // If there are unsaved changes, we warn the user before changing tabs.\n saveIfNecessary();\n });\n\n tabs.on('afterSwitch', (ev) => {\n currentTab = ev.newTab;\n const adventure = theAdventures[currentTab];\n\n if (!options.suppress_save_and_load_for_slides) {\n // Load initial code from local storage, if available\n const programFromLs = localLoad(currentTabLsKey());\n // if we are in raw (used in slides) we don't want to load from local storage, we always want to show startcode\n if (programFromLs && adventure) {\n adventure.editor_contents = programFromLs.code;\n adventure.save_name = programFromLs.saveName;\n adventure.save_info = 'local-storage';\n }\n }\n\n reconfigurePageBasedOnTab();\n checkNow();\n theLocalSaveWarning.switchTab();\n });\n\n initializeSpeech();\n\n if (options.start_tutorial) {\n startIntroTutorial();\n }\n\n // Share/hand in modals\n $('#share_program_button').on('click', () => $('#share-modal').show());\n $('#hand_in_button').on('click', () => $('#hand-in-modal').show());\n initializeShareProgramButtons();\n initializeHandInButton();\n\n if (options.suppress_save_and_load_for_slides) {\n disableAutomaticSaving();\n }\n\n // Save if user navigates away\n window.addEventListener('beforeunload', () => saveIfNecessary(), { capture: true });\n\n // Save if program name is changed\n $('#program_name').on('blur', () => saveIfNecessary());\n}\n\nfunction attachMainEditorEvents(editor: HedyEditor) {\n\n editor.on('change', () => {\n theLocalSaveWarning.setProgramLength(theGlobalEditor.contents.split('\\n').length);\n });\n\n // If prompt is shown and user enters text in the editor, hide the prompt.\n editor.on('change', function() {\n if (askPromptOpen) {\n stopit();\n theGlobalEditor.focus(); // Make sure the editor has focus, so we can continue typing\n }\n if ($('#ask-modal').is(':visible')) $('#inline-modal').hide();\n askPromptOpen = false;\n $('#runit').css('background-color', '');\n theGlobalEditor.clearErrors();\n theGlobalEditor.clearIncorrectLines();\n //removing the debugging state when loading in the editor\n stopDebug();\n });\n\n editor.on('click', (event: MouseEvent) => {\n editor.skipFaultyHandler(event);\n });\n\n // *** KEYBOARD SHORTCUTS ***\n let altPressed: boolean | undefined;\n // alt is 18, enter is 13\n window.addEventListener ('keydown', function (ev) {\n const keyCode = ev.keyCode;\n if (keyCode === 18) {\n altPressed = true;\n return;\n }\n if (keyCode === 13 && altPressed) {\n if (!theLevel || !theLanguage) {\n throw new Error('Oh no');\n }\n runit (theLevel, theLanguage, \"\", \"run\",function () {\n $ ('#output').focus ();\n });\n }\n // We don't use jquery because it doesn't return true for this equality check.\n if (keyCode === 37 && document.activeElement === document.getElementById ('output')) {\n theGlobalEditor.focus();\n theGlobalEditor.moveCursorToEndOfFile();\n }\n });\n window.addEventListener ('keyup', function (ev) {\n triggerAutomaticSave();\n const keyCode = ev.keyCode;\n if (keyCode === 18) {\n altPressed = false;\n return;\n }\n });\n}\n\nexport interface InitializeViewProgramPageOptions {\n readonly page: 'view-program';\n readonly level: number;\n readonly lang: string;\n readonly code: string;\n}\n\nexport function initializeViewProgramPage(options: InitializeViewProgramPageOptions) {\n theLevel = options.level;\n theLanguage = options.lang;\n\n // We need to enable the main editor for the program page as well\n const dir = $(\"body\").attr(\"dir\");\n theGlobalEditor = editorCreator.initializeEditorWithGutter($('#editor'), EditorType.MAIN, dir);\n initializeTranslation({\n keywordLanguage: options.lang,\n level: options.level\n });\n attachMainEditorEvents(theGlobalEditor);\n theGlobalEditor.contents = options.code;\n initializeDebugger({\n editor: theGlobalEditor,\n level: theLevel,\n language: theLanguage,\n keywordLanguage: theKeywordLanguage,\n });\n}\n\nexport function initializeHighlightedCodeBlocks(where: Element) {\n const dir = $(\"body\").attr(\"dir\");\n initializeParsons();\n if (theLevel) {\n initializeTranslation({\n keywordLanguage: theKeywordLanguage,\n level: theLevel\n })\n }\n // Any code blocks we find inside 'turn-pre-into-ace' get turned into\n // read-only editors (for syntax highlighting)\n for (const container of $(where).find('.turn-pre-into-ace').get()) {\n for (const preview of $(container).find('pre').get()) {\n $(preview)\n .addClass('relative text-lg rounded overflow-x-hidden')\n // We set the language of the editor to the current keyword_language -> needed when copying to main editor\n .attr('lang', theKeywordLanguage);\n\n // Only turn into an editor if the editor scrolls into view\n // Otherwise, the teacher manual Frequent Mistakes page is SUPER SLOW to load.\n onElementBecomesVisible(preview, () => {\n const codeNode = preview.querySelector('code')\n let code: string;\n // In case it has a child node\n if(codeNode) {\n codeNode.hidden = true\n code = codeNode.innerText\n } else {\n code = preview.textContent || \"\";\n preview.textContent = \"\";\n }\n\n // Create this example editor\n const exampleEditor = editorCreator.initializeReadOnlyEditor(preview, dir);\n // Strip trailing newline, it renders better\n exampleEditor.contents = code;\n exampleEditor.contents = exampleEditor.contents.trimEnd();\n // And add an overlay button to the editor if requested via a show-copy-button class, either\n // on the itself OR on the element that has the '.turn-pre-into-ace' class.\n if ($(preview).hasClass('show-copy-button') || $(container).hasClass('show-copy-button')) {\n const buttonContainer = $('').addClass('absolute ltr:right-0 rtl:left-0 top-0 mx-1 mt-1').appendTo(preview);\n let symbol = \"\u21E5\";\n if (dir === \"rtl\") {\n symbol = \"\u21E4\";\n }\n $('
').css({ fontFamily: 'sans-serif' }).addClass('yellow-btn').text(symbol).appendTo(buttonContainer).click(function() {\n if (!theGlobalEditor?.isReadOnly) {\n theGlobalEditor.contents = exampleEditor.contents + '\\n';\n }\n update_view(\"main_editor_keyword_selector\", $(preview).attr('lang'));\n stopit();\n clearOutput();\n });\n }\n const levelStr = $(preview).attr('level');\n const lang = $(preview).attr('lang');\n if (levelStr && lang) {\n initializeTranslation({\n keywordLanguage: lang,\n level: parseInt(levelStr, 10),\n })\n exampleEditor.setHighlighterForLevel(parseInt(levelStr, 10));\n }\n });\n }\n }\n}\n\nexport function getHighlighter(level: number) {\n return `ace/mode/level${level}`;\n}\n\nexport function stopit() {\n if (pygameRunning) {\n // when running pygame, raise the pygame quit event\n Sk.insertPyGameEvent(\"quit\");\n Sk.unbindPygameListeners();\n\n pygameRunning = false;\n document.onkeydown = null;\n $('#pygame-modal').hide();\n $('#stopit').hide();\n $('#runit').show();\n }\n else\n {\n // We bucket-fix stop the current program by setting the run limit to 1ms\n Sk.execLimit = 1;\n clearTimeouts();\n $('#stopit').hide();\n $('#runit').show();\n\n // This gets a bit complex: if we do have some input modal waiting, fake submit it and hide it\n // This way the Promise is no longer \"waiting\" and can no longer mess with our next program\n if ($('#ask-modal').is(\":visible\")) {\n $('#ask-modal form').submit();\n $('#ask-modal').hide();\n }\n }\n\n askPromptOpen = false;\n}\n\nfunction clearOutput() {\n const outputDiv = $('#output');\n //Saving the variable button because sk will overwrite the output div\n const variableButton = outputDiv.find('#variable_button');\n const variables = outputDiv.find('#variables');\n outputDiv.empty();\n\n outputDiv.addClass(\"overflow-auto\");\n outputDiv.append(variableButton);\n outputDiv.append(variables);\n error.hide();\n success.hide();\n\n // Clear the user created buttons.\n const buttonsDiv = $('#dynamic-buttons');\n buttonsDiv.empty();\n buttonsDiv.hide();\n}\n\nexport async function runit(level: number, lang: string, disabled_prompt: string, run_type: \"run\" | \"debug\" | \"continue\", cb: () => void) {\n // Copy 'currentTab' into a variable, so that our event handlers don't mess up\n // if the user changes tabs while we're waiting for a response\n const adventureName = currentTab;\n\n if (askPromptOpen) {\n // If there is no message -> don't show a prompt\n if (disabled_prompt) {\n return modal.notifyError(disabled_prompt);\n }\n return;\n }\n\n theLocalSaveWarning.clickRun();\n\n // Make sure to stop previous PyGame event listeners\n if (typeof Sk.unbindPygameListeners === 'function') {\n Sk.unbindPygameListeners();\n }\n\n // We set the run limit to 1ms -> make sure that the previous programs stops (if there is any)\n Sk.execLimit = 1;\n $('#runit').hide();\n $('#stopit').show();\n $('#saveFilesContainer').hide();\n\n if (run_type !== 'continue') {\n clearOutput();\n }\n\n try {\n var editor = theGlobalEditor;\n var code = \"\";\n if ($('#parsons_container').is(\":visible\")) {\n code = get_parsons_code();\n // We return no code if all lines are empty or there is a mistake -> clear errors and do nothing\n if (!code) {\n editor.clearErrors();\n stopit();\n return;\n } else {\n // Add the onclick on the button -> only show if there is another exercise to load (set with an onclick)\n if ($('#next_parson_button').attr('onclick')) {\n $('#next_parson_button').show();\n }\n }\n } else {\n code = get_active_and_trimmed_code();\n if (code.length == 0) {\n editor.clearErrors()\n stopit();\n return;\n }\n }\n\n editor.clearErrors()\n removeBulb();\n // console.log('Original program:\\n', code);\n\n const adventure = theAdventures[adventureName];\n let program_data;\n\n if (run_type === 'run' || run_type === 'debug') {\n try {\n cancelPendingAutomaticSave();\n let data = {\n level: `${level}`,\n code: code,\n lang: lang,\n skip_faulty: false,\n is_debug: run_type === 'debug',\n tutorial: $('#code_output').hasClass(\"z-40\"), // if so -> tutorial mode\n read_aloud : !!$('#speak_dropdown').val(),\n adventure_name: adventureName,\n\n // Save under an existing id if this field is set\n program_id: isServerSaveInfo(adventure?.save_info) ? adventure.save_info.id : undefined,\n save_name: saveNameFromInput(),\n };\n\n let response = await postJsonWithAchievements('/parse', data);\n\n program_data = response;\n console.log('Response', response);\n\n if (response.Warning && $('#editor').is(\":visible\")) {\n //storeFixedCode(response, level);\n error.showWarning(ClientMessages['Transpile_warning'], response.Warning);\n }\n\n showAchievements(response.achievements, false, \"\");\n if (adventure && response.save_info) {\n adventure.save_info = response.save_info;\n adventure.editor_contents = code;\n }\n\n if (response.Error) {\n error.show(ClientMessages['Transpile_error'], response.Error);\n if (response.Location && response.Location[0] != \"?\") {\n //storeFixedCode(response, level);\n // Location can be either [row, col] or just [row].\n theGlobalEditor.highlightError(response.Location[0], response.Location[1]);\n }\n $('#stopit').hide();\n $('#runit').show();\n return;\n }\n } catch (e: any) {\n console.error(e);\n if (e.internetError) {\n error.show(ClientMessages['Connection_error'], ClientMessages['CheckInternet']);\n } else {\n error.show(ClientMessages['Other_error'], ClientMessages['ServerError']);\n }\n }\n } else {\n program_data = theGlobalDebugger.get_program_data();\n }\n\n runPythonProgram(program_data.Code, program_data.source_map, program_data.has_turtle, program_data.has_pygame, program_data.has_sleep, program_data.has_clear, program_data.has_music, program_data.Warning, cb, run_type).catch(function(err: any) {\n // The err is null if we don't understand it -> don't show anything\n if (err != null) {\n error.show(ClientMessages['Execute_error'], err.message);\n reportClientError(level, code, err.message);\n }\n });\n\n\n } catch (e: any) {\n modal.notifyError(e.responseText);\n }\n}\n\nexport async function saveMachineFiles() {\n const response = await postJsonWithAchievements('/generate_machine_files', {\n level: theLevel,\n code: get_active_and_trimmed_code(),\n lang: theLanguage,\n });\n\n if (response.filename) {\n // Download the file\n window.location.replace('/download_machine_files/' + response.filename);\n }\n}\n\n// We've observed that this code may gets invoked 100s of times in quick succession. Don't\n// ever push the same achievement more than once per page load to avoid this.\nconst ACHIEVEMENTS_PUSHED: Record = {};\n\nexport async function pushAchievement(achievement: string) {\n if (ACHIEVEMENTS_PUSHED[achievement]) {\n return;\n }\n ACHIEVEMENTS_PUSHED[achievement] = true;\n\n try {\n const response = await postJson('/achievements/push-achievement', { achievement });\n showAchievements(response.achievements, false, \"\");\n } catch {\n // This might fail commonly with a 403 (not logged in). Ignore any errors anyway.\n }\n}\n\nexport function closeAchievement() {\n $('#achievement_pop-up').hide();\n if ($('#achievement_pop-up').attr('reload')) {\n $('#achievement_pop-up').removeAttr('reload');\n $('#achievement_pop-up').removeAttr('redirect');\n return location.reload();\n }\n if ($('#achievement_pop-up').attr('redirect')) {\n const redirect = $('#achievement_pop-up').attr('redirect');\n $('#achievement_pop-up').removeAttr('reload');\n $('#achievement_pop-up').removeAttr('redirect');\n return window.location.pathname = redirect;\n }\n // If for some reason both situation don't happen we still want to make sure the attributes are removed\n $('#achievement_pop-up').removeAttr('reload');\n $('#achievement_pop-up').removeAttr('redirect');\n}\n\nexport async function showAchievements(achievements: Achievement[] | undefined, reload: boolean, redirect: string) {\n if (!achievements || achievements.length === 0) {\n return;\n }\n\n for (const achievement of achievements) {\n await showAchievement(achievement);\n }\n\n if (reload) {\n $('#achievement_pop-up').attr('reload', 'true');\n setTimeout(function(){\n $('#achievement_pop-up').removeAttr('reload');\n $('#achievement_pop-up').removeAttr('redirect');\n location.reload();\n }, achievements.length * 6000);\n }\n if (redirect) {\n $('#achievement_pop-up').attr('redirect', redirect);\n setTimeout(function(){\n $('#achievement_pop-up').removeAttr('reload');\n $('#achievement_pop-up').removeAttr('redirect');\n window.location.pathname = redirect;\n }, achievements.length * 6000);\n }\n}\n\nfunction showAchievement(achievement: Achievement) {\n return new Promise((resolve)=>{\n $('#achievement_reached_title').text('\"' + achievement[0] + '\"');\n $('#achievement_reached_text').text(achievement[1]);\n $('#achievement_reached_statics').text(achievement[2]);\n $('#achievement_pop-up').fadeIn(1000, function () {\n setTimeout(function(){\n $('#achievement_pop-up').fadeOut(1000);\n }, 4000);\n });\n setTimeout(()=>{\n resolve();\n ;} , 6000\n );\n });\n}\n\nfunction removeBulb(){\n const repair_button = $('#repair_button');\n repair_button.hide();\n}\n\n/**\n * Called when the user clicks the \"Try\" button in one of the palette buttons\n */\nexport function tryPaletteCode(exampleCode: string) {\n if (theGlobalEditor?.isReadOnly) {\n return;\n }\n\n theGlobalEditor.contents = exampleCode + '\\n';\n //As the commands try-it buttons only contain english code -> make sure the selected language is english\n if (!($('#editor').attr('lang') == 'en')) {\n $('#editor').attr('lang', 'en');\n update_view(\"main_editor_keyword_selector\", \"en\");\n }\n}\n\nexport function viewProgramLink(programId: string) {\n return window.location.origin + '/hedy/' + programId + '/view';\n}\n\nexport async function delete_program(id: string, index: number, prompt: string) {\n await modal.confirmP(prompt);\n await tryCatchPopup(async () => {\n const response = await postJsonWithAchievements('/programs/delete', { id });\n showAchievements(response.achievement, true, \"\");\n $('#program_' + index).remove();\n modal.notifySuccess(response.message);\n });\n}\n\nfunction set_favourite(index: number) {\n $('.favourite_program_container').removeClass('text-yellow-400');\n $('.favourite_program_container').addClass('text-white');\n\n $('#favourite_program_container_' + index).removeClass('text-white');\n $('#favourite_program_container_' + index).addClass('text-yellow-400');\n}\n\nexport async function set_favourite_program(id: string, index: number, prompt: string) {\n await modal.confirmP(prompt);\n await tryCatchPopup(async () => {\n const response = await postJsonWithAchievements('/programs/set_favourite', { id });\n set_favourite(index)\n modal.notifySuccess(response.message);\n });\n}\n\nfunction change_to_submitted (index: number) {\n // Index is a front-end unique given to each program container and children\n // This value enables us to remove, hide or show specific element without connecting to the server (again)\n $('#non_submitted_button_container_' + index).remove();\n $('#submitted_button_container_' + index).show();\n $('#submitted_header_' + index).show();\n $('#program_' + index).removeClass(\"border-orange-400\");\n $('#program_' + index).addClass(\"border-gray-400 bg-gray-400\");\n}\n\nexport function submit_program (id: string, index: number) {\n tryCatchPopup(async () => {\n await postJsonWithAchievements('/programs/submit', { id });\n change_to_submitted(index);\n });\n}\n\nexport async function set_explore_favourite(id: string, favourite: number) {\n let prompt = \"Are you sure you want to remove this program as a \\\"Hedy\\'s choice\\\" program?\";\n if (favourite) {\n prompt = \"Are you sure you want to set this program as a \\\"Hedy\\'s choice\\\" program?\";\n }\n await modal.confirmP(prompt);\n\n await tryCatchPopup(async () => {\n const response = await postJsonWithAchievements('/programs/set_hedy_choice', {\n id: id,\n favourite: favourite\n });\n\n modal.notifySuccess(response.message);\n $('#' + id).toggleClass('text-white', favourite !== 1);\n $('#' + id).toggleClass('text-yellow-500', favourite === 1);\n });\n}\n\nexport function report_program(prompt: string, id: string) {\n tryCatchPopup(async () => {\n await modal.confirmP(prompt);\n const response = await postJsonWithAchievements('/programs/report', { id });\n modal.notifySuccess(response.message);\n });\n}\n\nexport function copy_to_clipboard (string: string, prompt: string) {\n // https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f\n var el = document.createElement ('textarea');\n el.value = string;\n el.setAttribute ('readonly', '');\n el.style.position = 'absolute';\n el.style.left = '-9999px';\n document.body.appendChild (el);\n\n const selection = document.getSelection();\n const originalSelection = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : undefined;\n\n el.select ();\n document.execCommand ('copy');\n document.body.removeChild (el);\n if (originalSelection) {\n document.getSelection()?.removeAllRanges ();\n document.getSelection()?.addRange (originalSelection);\n }\n\n // Hide all modals to make sure the copy clipboard modal is hidden as well -> show alert() with feedback\n modal.hide();\n modal.notifySuccess(prompt, 3000);\n}\n\n/**\n * Do a POST with the error to the server so we can log it\n */\nfunction reportClientError(level: number, code: string, client_error: string) {\n postJsonWithAchievements('/report_error', {\n level: `${level}`,\n code: code,\n page: window.location.href,\n client_error: client_error,\n });\n}\n\nwindow.onerror = function reportClientException(message, source, line_number, column_number, error) {\n postJsonWithAchievements('/client_exception', {\n message: message,\n source: source,\n line_number: line_number,\n column_number: column_number,\n error: error,\n url: window.location.href,\n user_agent: navigator.userAgent,\n });\n}\n\nexport function runPythonProgram(this: any, code: string, sourceMap: any, hasTurtle: boolean, hasPygame: boolean, hasSleep: boolean, hasClear: boolean, hasMusic: boolean, hasWarnings: boolean, cb: () => void, run_type: \"run\" | \"debug\" | \"continue\") {\n // If we are in the Parsons problem -> use a different output\n let outputDiv = $('#output');\n let skip_faulty_found_errors = false;\n let warning_box_shown = false;\n\n if (sourceMap){\n theGlobalSourcemap = sourceMap;\n // We loop through the mappings and underline a mapping if it contains an error\n for (const index in sourceMap) {\n const map = sourceMap[index];\n\n const range = {\n startLine: map.hedy_range.from_line,\n startColumn: map.hedy_range.from_column,\n endLine: map.hedy_range.to_line,\n endColumn: map.hedy_range.to_column\n }\n\n if (map.error != null) {\n skip_faulty_found_errors = true;\n theGlobalEditor.setIncorrectLine(range, Number(index));\n }\n\n // Only show the warning box for the first error shown\n if (skip_faulty_found_errors && !warning_box_shown) {\n error.showFadingWarning(ClientMessages['Execute_error'], ClientMessages['Errors_found']);\n warning_box_shown = true;\n }\n }\n }\n\n let skulptExternalLibraries:{[index: string]:any} = {\n './extensions.js': {\n path: theStaticRoot + \"/vendor/skulpt-stdlib-extensions.js\",\n },\n };\n\n Sk.pre = \"output\";\n const turtleConfig = (Sk.TurtleGraphics || (Sk.TurtleGraphics = {}));\n turtleConfig.target = 'turtlecanvas';\n // If the adventures are not shown -> increase height of turtleConfig\n if ($('#adventures-tab').is(\":hidden\")) {\n turtleConfig.height = 600;\n turtleConfig.worldHeight = 600;\n } else if ($('#turtlecanvas').attr(\"raw\") == 'yes'){\n turtleConfig.height = 150;\n turtleConfig.worldHeight = 250;\n }\n else {\n turtleConfig.height = 300;\n turtleConfig.worldHeight = 300;\n }\n // Always set the width to output panel width -> match the UI\n turtleConfig.width = outputDiv.width();\n turtleConfig.worldWidth = outputDiv.width();\n\n let code_prefix = normal_prefix;\n\n if (!hasTurtle && !hasPygame) {\n // There might still be a visible turtle panel. If the new program does not use the Turtle,\n // remove it (by clearing the '#turtlecanvas' div)\n $('#turtlecanvas').empty();\n }\n\n if (hasTurtle) {\n code_prefix += turtle_prefix;\n $('#turtlecanvas').show();\n }\n\n if (hasMusic) {\n code_prefix += music_prefix;\n $('#turtlecanvas').show();\n }\n\n if (hasPygame){\n skulptExternalLibraries = {\n './extensions.js': {\n path: theStaticRoot + \"/vendor/skulpt-stdlib-extensions.js\",\n },\n './pygame.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/init.js\",\n },\n './display.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/display.js\",\n },\n './draw.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/draw.js\",\n },\n './event.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/event.js\",\n },\n './font.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/font.js\",\n },\n './image.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/image.js\",\n },\n './key.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/key.js\",\n },\n './mouse.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/mouse.js\",\n },\n './transform.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/transform.js\",\n },\n './locals.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/locals.js\",\n },\n './time.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/time.js\",\n },\n './version.js': {\n path: theStaticRoot + \"/vendor/pygame_4_skulpt/version.js\",\n },\n './buttons.js': {\n path: theStaticRoot + \"/js/buttons.js\",\n },\n };\n\n code_prefix += pygame_prefix;\n\n initSkulpt4Pygame();\n initCanvas4PyGame();\n let pygameModal = $('#pygame-modal');\n\n const codeContainsInputFunctionBeforePygame = new RegExp(\n \"input\\\\([\\\\s\\\\S]*\\\\)[\\\\s\\\\S]*while not pygame_end\", 'gm'\n ).test(code);\n\n if (!codeContainsInputFunctionBeforePygame) {\n pygameModal.show();\n }\n\n if (hasTurtle) {\n pygameModal.addClass('absolute');\n pygameModal.addClass('bottom-0');\n pygameModal.addClass('w-full');\n } else {\n pygameModal.removeClass('absolute');\n pygameModal.removeClass('bottom-0');\n pygameModal.removeClass('w-full');\n }\n\n document.onkeydown = animateKeys;\n pygameRunning = true;\n }\n\n code = code_prefix + code;\n if (hasPygame) code += pygame_suffix;\n\n if (run_type === \"run\") {\n Sk.configure({\n output: outf,\n read: builtinRead,\n inputfun: inputFromInlineModal,\n inputfunTakesPrompt: true,\n setTimeout: timeout,\n __future__: Sk.python3,\n timeoutMsg: function () {\n // If the timeout is 1 this is due to us stopping the program: don't show \"too long\" warning\n $('#stopit').hide();\n $('#runit').show();\n $('#runit').show();\n if (Sk.execLimit != 1) {\n pushAchievement(\"hedy_hacking\");\n return ClientMessages ['Program_too_long'];\n } else {\n return null;\n }\n },\n // We want to make the timeout function a bit more sophisticated that simply setting a value\n // In levels 1-6 users are unable to create loops and programs with a lot of lines are caught server-sided\n // So: a very large limit in these levels, keep the limit on other ones.\n execLimit: (function () {\n const level = theLevel;\n if (hasTurtle || hasPygame || hasMusic) {\n // We don't want a timeout when using the turtle or pygame or music -> just set one for 10 minutes\n return (6000000);\n }\n if (level < 7) {\n // Also on a level < 7 (as we don't support loops yet), a timeout is redundant -> just set one for 5 minutes\n return (3000000);\n }\n // Set a time-out of either 20 seconds when having a sleep and 5 seconds when not\n return ((hasSleep) ? 20000 : 5000);\n }) ()\n });\n\n (Sk as any).builtins.play = new Sk.builtin.func((notes:any) => {\n //const now = Tone.now()\n const note_name = notes.v;\n\n //play note_name for the duration of an 16th note\n synth.triggerAttackRelease(note_name, \"16n\");\n\n });\n\n return Sk.misceval.asyncToPromise(() =>\n Sk.importMainWithBody(\"\", false, code, true), {\n \"*\": () => {\n // We don't do anything here...\n }\n }\n ).then(function(_mod) {\n console.log('Program executed');\n const pythonVariables = Sk.globals;\n load_variables(pythonVariables);\n $('#stopit').hide();\n $('#runit').show();\n\n if (hasPygame) {\n document.onkeydown = null;\n $('#pygame-modal').hide();\n }\n\n if (hasTurtle) {\n $('#saveFilesContainer').show();\n }\n\n // Check if the program was correct but the output window is empty: Return a warning\n if ((!hasClear) && $('#output').is(':empty') && $('#turtlecanvas').is(':empty')) {\n pushAchievement(\"error_or_empty\");\n error.showWarning(ClientMessages['Transpile_warning'], ClientMessages['Empty_output']);\n return;\n }\n if (!hasWarnings && code !== last_code) {\n showSuccesMessage(); //FH nov 2023: typo in success :)\n last_code = code;\n }\n if (cb) cb ();\n }).catch(function(err) {\n const errorMessage = errorMessageFromSkulptError(err) || null;\n if (!errorMessage) {\n throw null;\n }\n throw new Error(errorMessage);\n });\n\n } else if (run_type === \"debug\") {\n\n theGlobalDebugger = new Sk.Debugger('', incrementDebugLine, stopDebug);\n theGlobalSourcemap = sourceMap;\n\n Sk.configure({\n output: outf,\n read: builtinRead,\n inputfun: inputFromInlineModal,\n inputfunTakesPrompt: true,\n __future__: Sk.python3,\n debugging: true,\n breakpoints: theGlobalDebugger.check_breakpoints.bind(theGlobalDebugger),\n execLimit: null\n });\n\n let lines = code.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n // lines with dummy variable name are not meant to be shown to the user, skip them.\n if (lines[i].includes(\"# __BREAKPOINT__\") && !lines[i].includes('x__x__x__x')) {\n // breakpoints are 1-indexed\n theGlobalDebugger.add_breakpoint('.py', i + 1, '0', false);\n }\n }\n\n // Do not show success message if we found errors that we skipped\n if (!hasWarnings && code !== last_code && !skip_faulty_found_errors) {\n last_code = code;\n }\n\n theGlobalDebugger.set_code_starting_line(code_prefix.split('\\n').length - 1);\n theGlobalDebugger.set_code_lines(code.split('\\n'));\n theGlobalDebugger.set_program_data({\n Code: code,\n source_map: sourceMap,\n has_turtle: hasTurtle,\n has_pygame: hasPygame, //here too: where is hassleep?\n has_clear: hasClear,\n has_music: hasMusic,\n Warning: hasWarnings\n });\n\n startDebug();\n\n return theGlobalDebugger.startDebugger(\n () => Sk.importMainWithBody(\"\", false, code, true),\n theGlobalDebugger\n ).then(\n function () {\n console.log('Program executed');\n\n $('#stopit').hide();\n $('#runit').show();\n\n stopDebug();\n\n if (hasPygame) {\n document.onkeydown = null;\n $('#pygame-modal').hide();\n }\n\n if (hasTurtle) {\n $('#saveFilesContainer').show();\n }\n\n if (cb) cb ();\n }\n ).catch(function(err: any) {\n const errorMessage = errorMessageFromSkulptError(err) || null;\n if (!errorMessage) {\n throw null;\n }\n throw new Error(errorMessage);\n });\n\n } else {\n // maybe remove debug marker here\n return theGlobalDebugger.continueForward()\n .catch(function(err: any) {\n console.error(err)\n const errorMessage = errorMessageFromSkulptError(err) || null;\n if (!errorMessage) {\n throw null;\n }\n throw new Error(errorMessage);\n });\n }\n\n /**\n * Get the error messages from a Skulpt error\n *\n * They look like this:\n *\n * {\"args\":{\"v\":[{\"v\":\"name 'name' is not defined\"}]},\"traceback\":[{\"lineno\":3,\"colno\":0,\"filename\":\".py\"}]}\n *\n * Don't know why, so let's be defensive about it.\n */\n function errorMessageFromSkulptError(err: any) {\n const message = err.args && err.args.v && err.args.v[0] && err.args.v[0].v;\n return message;\n }\n\n function addToOutput(text: string, color: string) {\n $('').text(text).css({ color }).appendTo(outputDiv);\n scrollOutputToBottom();\n }\n\n // output functions are configurable. This one just appends some text\n // to a pre element.\n function outf(text: string) {\n addToOutput(text, 'white');\n speak(text)\n }\n\n function builtinRead(x: string) {\n if (x in skulptExternalLibraries) {\n const tmpPath = skulptExternalLibraries[x][\"path\"];\n if (x === \"./pygame.js\") {\n return Sk.misceval.promiseToSuspension(\n fetch(tmpPath)\n .then(r => r.text()))\n\n } else {\n let request = new XMLHttpRequest();\n request.open(\"GET\", tmpPath, false);\n request.send();\n\n if (request.status !== 200) {\n return void 0\n }\n\n return request.responseText\n }\n }\n\n if (Sk.builtinFiles === undefined || Sk.builtinFiles[\"files\"][x] === undefined)\n throw \"File not found: '\" + x + \"'\";\n return Sk.builtinFiles[\"files\"][x];\n }\n\n // This method draws the prompt for asking for user input.\n function inputFromInlineModal(prompt: string) {\n // We give the user time to give input.\n var storage = window.localStorage;\n var debug = storage.getItem(\"debugLine\")\n if (storage.getItem(\"prompt-\" + prompt) == null) {\n Sk.execStart = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 365);\n $('#turtlecanvas').hide();\n\n if (pygameRunning) {\n Sk.unbindPygameListeners();\n document.onkeydown = null;\n $('#pygame-modal').hide();\n }\n\n return new Promise(function(ok) {\n askPromptOpen = true;\n\n const input = $('#ask-modal input[type=\"text\"]');\n $('#ask-modal .caption').text(prompt);\n input.val('');\n input.attr('placeholder', prompt);\n speak(prompt)\n\n setTimeout(function() {\n input.focus();\n }, 0);\n $('#ask-modal form').one('submit', function(event) {\n askPromptOpen = false;\n event.preventDefault();\n $('#ask-modal').hide();\n\n if (hasTurtle) {\n $('#turtlecanvas').show();\n }\n\n if (pygameRunning) {\n Sk.bindPygameListeners();\n document.onkeydown = animateKeys;\n\n if (!hasTurtle) {\n $('#pygame-modal').show();\n }\n }\n\n // We reset the timer to the present moment.\n Sk.execStart = new Date ();\n // We set a timeout for sending back the input, so that the input box is hidden before processing the program.\n // Since processing the program might take some time, this timeout increases the responsiveness of the UI after\n // replying to a query.\n setTimeout (function () {\n ok(input.val());\n if (debug != null) {\n storage.setItem(\"prompt-\" + prompt, input.val()!.toString());\n }\n $ ('#output').focus ();\n }, 0);\n\n return false;\n });\n $('#ask-modal').show();\n\n // Scroll the output div to the bottom so you can see the question\n scrollOutputToBottom();\n });\n } else {\n return new Promise(function (ok) {\n ok(storage.getItem(\"prompt-\" + prompt));\n });\n }\n }\n}\n\nfunction resetTurtleTarget() {\n if (Sk.TurtleGraphics !== undefined) {\n\n let selector = Sk.TurtleGraphics.target;\n let target = typeof selector === \"string\" ? document.getElementById(selector) : selector;\n if (target !== null && target !== undefined){\n // clear canvas container\n while (target.firstChild) {\n target.removeChild(target.firstChild);\n }\n return target;\n }\n\n }\n\n return null;\n}\n\nfunction animateKeys(event: KeyboardEvent) {\n const keyColors = ['#cbd5e0', '#bee3f8', '#4299e1', '#ff617b', '#ae81ea', '#68d391'];\n const output = $(\"#output\");\n\n if (output !== null) {\n let keyElement = $(\"
\");\n output.append(keyElement);\n\n keyElement.text(event.key);\n keyElement.css('color', keyColors[Math.floor(Math.random() * keyColors.length)]);\n keyElement.addClass('animate-keys')\n\n setTimeout(function () {\n keyElement.remove()\n }, 1500);\n }\n}\n\nfunction initCanvas4PyGame() {\n let currentTarget = resetTurtleTarget();\n\n let div1 = document.createElement(\"div\");\n\n if (currentTarget !== null) {\n currentTarget.appendChild(div1);\n $(div1).addClass(\"modal\");\n $(div1).css(\"text-align\", \"center\");\n $(div1).css(\"display\", \"none\");\n\n let div2 = document.createElement(\"div\");\n $(div2).addClass(\"modal-dialog modal-lg\");\n $(div2).css(\"display\", \"inline-block\");\n\n // I'm not sure what the code below was supposed to do,\n // but it was referring to 'self.width' which does not\n // exist, and the result would be 'undefined + 42 == NaN'.\n //\n // (as any to make TypeScript allow the nonsensical addition)\n $(div2).width(undefined as any + 42);\n $(div2).attr(\"role\", \"document\");\n div1.appendChild(div2);\n\n let div3 = document.createElement(\"div\");\n $(div3).addClass(\"modal-content\");\n div2.appendChild(div3);\n\n let div4 = document.createElement(\"div\");\n $(div4).addClass(\"modal-header d-flex justify-content-between\");\n let div5 = document.createElement(\"div\");\n $(div5).addClass(\"modal-body\");\n let div6 = document.createElement(\"div\");\n $(div6).addClass(\"modal-footer\");\n let div7 = document.createElement(\"div\");\n $(div7).addClass(\"col-md-8\");\n let div8 = document.createElement(\"div\");\n $(div8).addClass(\"col-md-4\");\n\n div3.appendChild(div4);\n div3.appendChild(div5);\n div3.appendChild(div6);\n\n $(Sk.main_canvas).css(\"border\", \"none\");\n $(Sk.main_canvas).css(\"display\", \"none\");\n div5.appendChild(Sk.main_canvas);\n }\n}\n\nfunction initSkulpt4Pygame() {\n Sk.main_canvas = document.createElement(\"canvas\");\n Sk.configure({\n killableWhile: true,\n killableFor: true,\n __future__: Sk.python3,\n });\n}\n\nfunction speak(text: string) {\n var selectedURI = $('#speak_dropdown').val();\n if (!selectedURI) { return; }\n var voice = window.speechSynthesis.getVoices().filter(v => v.voiceURI === selectedURI)[0];\n\n if (voice) {\n let utterance = new SpeechSynthesisUtterance(text);\n utterance.voice = voice;\n utterance.rate = 0.9;\n speechSynthesis.speak(utterance);\n }\n pushAchievement(\"make_some_noise\");\n}\n\nfunction initializeSpeech() {\n // If we are running under cypress, always show the languages dropdown (even if the browser doesn't\n // have TTS capabilities), so that we can test if the logic for showing the dropdown at least runs\n // successfully.\n const isBeingTested = !!(window as any).Cypress;\n\n if (!window.speechSynthesis && !isBeingTested) { return; /* No point in even trying */ }\n if (!theLanguage) { return; /* Not on a code page */ }\n\n /**\n * Show the \"speak\" checkbox if we find that we have speech support for the\n * current language (showing an initially hidden element is a better experience\n * than hiding an initially shown element... arguably... ?)\n *\n * Also, for funzies: the speechSynthesis.getVoices() array is asynchronously\n * populated *some time* after the page loads... and we won't know when. Keep\n * on testing periodically until we got it or it's taken too long to finish.\n */\n let attempts = 0;\n const timer = setInterval(function() {\n attempts += 1;\n\n const voices = findVoices(theLanguage);\n\n if (voices.length > 0 || isBeingTested) {\n for (const voice of voices) {\n $('#speak_dropdown').append($('').attr('value', voice.voiceURI).text('\uD83D\uDCE3 ' + voice.name));\n }\n\n $('#speak_container').show();\n\n clearInterval(timer);\n }\n if (attempts >= 20) { // ~2 seconds\n // Give up\n clearInterval(timer);\n }\n }, 100);\n\n function findVoices(lang: string) {\n // Our own \"lang\" is *typically* just the language code, but we also have \"pt_BR\".\n const m = lang.match(/^([a-z]+)/i);\n if (!m) { return []; }\n const simpleLang = m[1];\n\n // If the feature doesn't exist in the browser, return null\n if (!window.speechSynthesis) { return []; }\n return window.speechSynthesis.getVoices().filter(voice => voice.lang.startsWith(simpleLang));\n }\n}\n\nexport function load_quiz(level: string) {\n $('*[data-tabtarget=\"quiz\"]').html ('');\n}\n\nexport function showVariableView() {\n// When blue label button is clicked, the view will appear or hide\n const variables = $('#variables');\n if (variables.is(\":hidden\")) {\n variables.show();\n $(\"#variables\").trigger(\"click\")\n }\n else {\n variables.hide();\n }\n}\n\nexport async function store_parsons_attempt(order: Array, correct: boolean) {\n try {\n await postJsonWithAchievements('/store_parsons_order', {\n level: theLevel,\n exercise: $('#next_parson_button').attr('current_exercise'),\n order: order,\n correct: correct\n });\n } catch (e) {\n // Let's do nothing: saving is not a user relevant action -> no feedback required\n console.error(e);\n };\n}\n\nexport function get_active_and_trimmed_code() {\n theGlobalEditor.trimTrailingSpace();\n const storage = window.localStorage;\n const debugLine = storage.getItem(\"debugLine\");\n return theGlobalEditor.getActiveContents(debugLine);\n}\n\nexport function getEditorContents() {\n return theGlobalEditor.contents;\n}\n\nexport function confetti_cannon(){\n const canvas = document.getElementById('confetti');\n if (canvas) {\n canvas.classList.remove('hidden');\n // ignore this error, the function comes from CDN for now\n const jsConfetti = new JSConfetti({canvas})\n // timeout for the confetti to fall down\n setTimeout(function(){canvas.classList.add('hidden')}, 3000);\n let adventures = $('#adventures');\n let currentAdventure = $(adventures).find('.tab-selected').attr('data-tab');\n let customLevels = ['turtle', 'rock', 'haunted', 'restaurant', 'fortune', 'songs', 'dice']\n\n if(customLevels.includes(currentAdventure!)){\n let currentAdventureConfetti = getConfettiForAdventure(currentAdventure ?? '' as any);\n\n jsConfetti.addConfetti({\n emojis: currentAdventureConfetti,\n emojiSize: 45,\n confettiNumber: 100,\n });\n }\n else {\n jsConfetti.addConfetti();\n }\n\n const confettiButton = document.getElementById('confetti-button');\n if (confettiButton) {\n confettiButton.classList.add('hidden');\n }\n }\n}\n\nfunction getConfettiForAdventure(adventure: MessageKey){\n if (ClientMessages[adventure]) {\n return Array.from(ClientMessages[adventure]).filter(x => x !== ',' && x !== ' ');\n }\n return [['\uD83C\uDF08'], ['\u26A1\uFE0F'], ['\uD83D\uDCA5'], ['\u2728'], ['\uD83D\uDCAB']];\n}\n\n/**\n * Scroll the output to bottom immediately\n */\nfunction scrollOutputToBottom() {\n const outputDiv = $('#output');\n outputDiv.scrollTop(outputDiv.prop('scrollHeight'));\n}\n\nexport function modalStepOne(level: number){\n createModal(level);\n let $modalEditor = $('#modal-editor');\n if ($modalEditor.length) {\n const dir = $(\"body\").attr(\"dir\");\n theModalEditor = editorCreator.initializeEditorWithGutter($modalEditor, EditorType.MODAL, dir);\n }\n}\n\nfunction showSuccesMessage(){\n removeBulb();\n var allsuccessmessages = ClientMessages['Transpile_success'].split('\\n');\n var randomnum: number = Math.floor(Math.random() * allsuccessmessages.length);\n success.show(allsuccessmessages[randomnum]);\n}\n\nfunction createModal(level:number ){\n let editor = \"
\".replace(\"{level}\", level.toString());\n let title = ClientMessages['Program_repair'];\n modal.repair(editor, 0, title);\n}\n\nexport function toggleDevelopersMode(event='click', enforceDevMode: boolean) {\n let enable: boolean = false;\n switch (event) {\n case 'load':\n const lastSelection = window.localStorage.getItem('developer_mode') === 'true';\n enable = enforceDevMode || lastSelection;\n $('#developers_toggle').prop('checked', enable);\n break;\n\n case 'click':\n // Toggled\n enable = $('#developers_toggle').prop('checked');\n window.localStorage.setItem('developer_mode', `${enable}`);\n if (enable) {\n pushAchievement(\"lets_focus\");\n }\n break;\n }\n\n // DevMode hides the tabs and makes resizable elements track the appropriate size.\n // (Driving from HTML attributes is more flexible on what gets resized, and avoids duplicating\n // size literals between HTML and JavaScript).\n $('#adventures').toggle(!enable);\n // Parsons dont need a fixed height\n if (currentTab === 'parsons') return\n $('[data-devmodeheight]').each((_, el) => {\n const heights = $(el).data('devmodeheight').split(',') as string[];\n $(el).css('height', heights[enable ? 1 : 0]);\n });\n}\n\n/**\n * Run a code block, show an error message if we catch an exception\n */\nexport async function tryCatchErrorBox(cb: () => void | Promise) {\n try {\n return await cb();\n } catch (e: any) {\n console.log('Error', e);\n error.show(ClientMessages['Transpile_error'], e.message);\n }\n}\n\nexport function toggle_keyword_language(current_lang: string, new_lang: string) {\n tryCatchErrorBox(async () => {\n const response = await postJsonWithAchievements('/translate_keywords', {\n code: theGlobalEditor.contents,\n start_lang: current_lang,\n goal_lang: new_lang,\n level: theLevel,\n });\n\n if (response.success) {\n const code = response.code\n theGlobalEditor.contents = code;\n const saveName = saveNameFromInput();\n\n // save translated code to local storage\n // such that it can be fetched after reload\n localSave(currentTabLsKey(), { saveName, code });\n $('#editor').attr('lang', new_lang);\n\n // update the whole page (example codes)\n const hash = window.location.hash;\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n urlParams.set('keyword_language', new_lang)\n window.location.search = urlParams.toString()\n window.open(hash, \"_self\");\n }\n });\n}\n\nexport function toggle_blur_code() {\n // Switch the both icons from hiding / showing\n $('.blur-toggle').toggle();\n\n // Keep track of a element attribute \"blurred\" to indicate if blurred or not\n if ($('#editor').attr('blurred') == 'true') {\n $('#editor').css(\"filter\", \"\");\n $('#editor').css(\"-webkit-filter\", \"\");\n $('#editor').attr('blurred', 'false');\n } else {\n $('#editor').css(\"filter\", \"blur(3px)\");\n $('#editor').css(\"-webkit-filter\", \"blur(3px)\");\n $('#editor').attr('blurred', 'true');\n }\n}\n\nexport async function change_language(lang: string) {\n await tryCatchPopup(async () => {\n const response = await postJsonWithAchievements('/change_language', { lang });\n if (response.succes) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n if (urlParams.get('keyword_language') !== null) {\n urlParams.set('keyword_language', 'en');\n }\n if (urlParams.get(\"language\") !== null) {\n urlParams.set(\"language\", lang)\n window.location.search = urlParams.toString();\n } else {\n location.reload();\n }\n // What's the logic behind this? what happens the keyword_language=en and we want to change the language to arabic? params?\n // Check if keyword_language is set to change it to English\n // if (urlParams.get('keyword_language') !== null) {\n // urlParams.set('keyword_language', 'en');\n // window.location.search = urlParams.toString();\n // } else {\n // location.reload();\n // }\n }\n });\n}\n\n/**\n * Post JSON, return the result on success, throw an exception on failure\n *\n * Automatically handles any achievements the server might send our way.\n */\nasync function postJsonWithAchievements(url: string, data: any): Promise {\n const response = await postJson(url, data);\n showAchievements(response.achievement, true, \"\");\n return response;\n}\n\n\nfunction update_view(selector_container: string, new_lang: string) {\n $('#' + selector_container + ' > div').map(function() {\n if ($(this).attr('lang') == new_lang) {\n $(this).show();\n } else {\n $(this).hide();\n }\n });\n}\n\nexport function select_profile_image(image: number) {\n $('.profile_image').removeClass(\"border-2 border-blue-600\");\n $('#profile_image_' + image).addClass(\"border-2 border-blue-600\");\n $('#image').val(image);\n}\n\nexport function filter_admin() {\n const params: Record = {};\n\n const filter = $('#admin_filter_category').val();\n params['filter'] = filter;\n\n if ($('#hidden-page-input').val()) {\n params['page'] = $('#hidden-page-input').val();\n }\n\n switch (filter) {\n case 'email':\n case 'username':\n params['substring'] = $('#email_filter_input').val();\n break;\n case 'language':\n params['language'] = $('#language_filter_input').val();\n break;\n case 'keyword_language':\n params['keyword_language'] = $('#keyword_language_filter_input').val();\n break;\n default:\n params['start'] = $('#admin_start_date').val();\n params['end'] = $('#admin_end_date').val();\n break;\n }\n\n const queryString = Object.entries(params).map(([k, v]) => k + '=' + encodeURIComponent(v)).join('&');\n window.open('?' + queryString, '_self');\n}\n\nexport function hide_editor() {\n $('#fold_in_toggle_container').hide();\n $('#code_editor').toggle();\n $('#code_output').addClass('col-span-2');\n $('#fold_out_toggle_container').show();\n}\n\nexport function show_editor() {\n $('#fold_out_toggle_container').hide();\n $('#code_editor').toggle();\n $('#code_output').removeClass('col-span-2');\n $('#fold_in_toggle_container').show();\n}\n\n// See https://github.com/skulpt/skulpt/pull/579#issue-156538278 for the JS version of this code\n// We support multiple timers, even though it's unlikely we would ever need them\nlet timers: number[] = [];\n\nconst timeout = (func: () => void, delay: number) => {\n let id: number;\n const wrapper = () => {\n let idx = timers.indexOf(id);\n if (idx > -1) {\n timers.splice(idx, 1);\n }\n func();\n };\n id = window.setTimeout(wrapper, delay);\n timers.push(id);\n};\n\nconst clearTimeouts = () => {\n timers.forEach(clearTimeout);\n timers = [];\n};\n\nexport function downloadSlides(level: number) {\n var iframe : any = document.getElementById(`level-${level}-slides`)!;\n iframe.setAttribute('src',`/slides/${level}`);\n $(`#level-${level}-slides`).on('load', function (){\n var innerDoc = iframe.contentDocument || iframe.contentWindow.document;\n var slides = innerDoc.getElementsByTagName('section');\n var slidesHTML = ''\n for (let i = 0; i < slides.length; i++) {\n var innerIframe = slides[i].getElementsByTagName('iframe');\n for (let j = 0; j < innerIframe.length; j++) {\n var a = document.createElement('a');\n a.href = 'https://www.hedy.org' + innerIframe[j].getAttribute('src');\n a.appendChild(document.createTextNode(a.href));\n slides[i].appendChild(a);\n slides[i].removeChild(innerIframe[j]);\n }\n slidesHTML += '\\n'+ slides[i].outerHTML;\n }\n\n var template = slides_template.replace('{replace}', slidesHTML);\n var zip = JSZip();\n zip.file('index.html', template);\n zip.folder(\"lib\");\n zip.folder(`hedy-level-${level}`);\n zip.generateAsync({type: 'blob'})\n .then(function(content: any) {\n download(content, `hedy-level-${level}.zip`, \"zip\");\n });\n })\n}\n\nfunction download(data: any, filename: any, type: any) {\n var file = new Blob([data], {type: type});\n var a = document.createElement(\"a\"),\n url = URL.createObjectURL(file);\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n setTimeout(function() {\n document.body.removeChild(a);\n window.URL.revokeObjectURL(url);\n }, 0);\n}\n\n/**\n * Hide all things that may have been dynamically shown when switching tabs\n *\n * Reset the state of the editor.\n */\nfunction resetWindow() {\n $('#warningbox').hide ();\n $('#errorbox').hide ();\n $('#okbox').hide ();\n $('#repair_button').hide();\n const output = $('#output');\n const variable_button = $(output).find('#variable_button');\n const variables = $(output).find('#variables');\n output.empty();\n $('#turtlecanvas').empty();\n output.append(variable_button);\n output.append(variables);\n theGlobalEditor?.clearSelection();\n theGlobalEditor?.clearBreakpoints();\n}\n\n/**\n * Update page element visibilities/states based on the state of the current tab\n */\nfunction updatePageElements() {\n const isCodeTab = !(currentTab === 'quiz' || currentTab === 'parsons');\n\n // .toggle(bool) sets visibility based on the boolean\n\n // Explanation area is visible for non-code tabs, or when we are NOT in developer's mode\n $('#adventures-tab').toggle(!(isCodeTab && $('#developers_toggle').is(\":checked\")));\n $('#developers_toggle_container').toggle(isCodeTab);\n $('#level-header input').toggle(isCodeTab);\n $('#parsons_code_container').toggle(currentTab === 'parsons');\n $('#editor-area').toggle(isCodeTab || currentTab === 'parsons');\n $('#editor').toggle(isCodeTab);\n $('#debug_container').toggle(isCodeTab);\n $('#program_name_container').toggle(isCodeTab);\n theGlobalEditor.isReadOnly = false;\n\n const adventure = theAdventures[currentTab];\n if (adventure) {\n const saveInfo: ServerSaveInfo = isServerSaveInfo(adventure.save_info)\n ? adventure.save_info\n : { id : '*dummy*' };\n\n // SHARING SETTINGS\n // Star on \"share\" button is filled if program is already public, outlined otherwise\n const isPublic = !!saveInfo.public;\n $('#share_program_button')\n .toggleClass('active-bluebar-btn', isPublic);\n $(`#share-${isPublic ? 'public' : 'private'}`).prop('checked', true);\n\n // Show <...data-view=\"if-public-url\"> only if we have a public url\n $('[data-view=\"if-public\"]').toggle(isPublic);\n $('[data-view=\"if-public-url\"]').toggle(!!saveInfo.public_url);\n $('input[data-view=\"public-url\"]').val(saveInfo.public_url ?? '');\n\n // Paper plane on \"hand in\" button is filled if program is already submitted, outlined otherwise\n const isSubmitted = !!saveInfo.submitted;\n $('#hand_in_button')\n .toggleClass('active-bluebar-btn', isSubmitted);\n\n // Show <...data-view=\"if-submitted\"> only if we have a public url\n $('[data-view=\"if-submitted\"]').toggle(isSubmitted);\n $('[data-view=\"if-not-submitted\"]').toggle(!isSubmitted);\n\n theGlobalEditor.isReadOnly = isSubmitted;\n }\n}\n\n/**\n * After switching tabs, show/hide elements\n */\nfunction reconfigurePageBasedOnTab() {\n resetWindow();\n\n updatePageElements();\n if (currentTab === 'parsons') {\n loadParsonsExercise(theLevel, 1);\n // remove the fixed height from the editor\n document.getElementById('code_editor')!.style.height = '100%'\n document.getElementById('code_output')!.style.height = '100%'\n return;\n } else {\n $('[data-devmodeheight]').each((_, el) => {\n const heights = $(el).data('devmodeheight').split(',') as string[];\n $(el).css('height', heights[0]);\n });\n }\n\n const adventure = theAdventures[currentTab];\n if (adventure) {\n $ ('#program_name').val(adventure.save_name);\n theGlobalEditor.contents = adventure.editor_contents;\n }\n}\n\n/**\n * Find the containing modal for the event target, and close it\n *\n * The modal will be the containing HTML element that has data-modal=\"true\".\n *\n * Intended to be used from HTML: click=\"hedyApp.closeContainingModal(this)\"\n */\nexport function closeContainingModal(target: HTMLElement) {\n $(target).closest('[data-modal=\"true\"]').hide();\n}\n\nfunction initializeShareProgramButtons() {\n $('input[type=\"radio\"][name=\"public\"]').on('change', (ev) => {\n if ((ev.target as HTMLInputElement).checked) {\n const isPublic = $(ev.target).val() === '1' ? true : false;\n\n // Async-safe copy of current tab\n const adventure = theAdventures[currentTab];\n\n tryCatchPopup(async () => {\n await saveIfNecessary();\n\n const saveInfo = isServerSaveInfo(adventure?.save_info) ? adventure?.save_info : undefined;\n if (!saveInfo) {\n throw new Error('This program does not have an id');\n }\n\n const response = await postJsonWithAchievements('/programs/share', {\n id: saveInfo.id,\n public: isPublic,\n });\n\n modal.notifySuccess(response.message);\n if (response.save_info) {\n adventure.save_info = response.save_info;\n }\n updatePageElements();\n });\n }\n })\n}\n\nfunction initializeHandInButton() {\n $('#do_hand_in_button').on('click', () => {\n // Async-safe copy of current tab\n const adventure = theAdventures[currentTab];\n\n tryCatchPopup(async () => {\n await saveIfNecessary();\n\n const saveInfo = isServerSaveInfo(adventure?.save_info) ? adventure.save_info : undefined;\n if (!saveInfo) {\n throw new Error('This program does not have an id');\n }\n const response = await postJsonWithAchievements('/programs/submit', {\n id: saveInfo.id,\n });\n\n modal.notifySuccess(response.message);\n if (response.save_info) {\n adventure.save_info = response.save_info;\n }\n updatePageElements();\n });\n });\n}\n\n/**\n * Initialize copy to clipboard buttons.\n *\n * For all elements with data-action=\"copy-to-clipboard\", find the containing\n * data-copy=\"container\" items and an in there, and copy it to the clipboard.\n */\nfunction initializeCopyToClipboard() {\n $('[data-action=\"copy-to-clipboard\"]').on('click', (ev) => {\n const text = $(ev.target).closest('[data-copy=\"container\"]').find('input').val();\n if (typeof text === 'string') {\n copy_to_clipboard(text, ClientMessages.copy_link_to_share);\n }\n });\n}\n\nfunction saveNameFromInput(): string {\n return $('#program_name').val() as string;\n}\n\nfunction programNeedsSaving(adventureName: string) {\n const adventure = theAdventures[adventureName];\n if (!adventure) {\n return false;\n }\n\n // We need to save if the content changed, OR if we have the opportunity to\n // save a program that was loaded from local storage to the server.\n // (Submitted programs are never saved again).\n const programChanged = theGlobalEditor.contents !== adventure.editor_contents;\n const nameChanged = $('#program_name').val() !== adventure.save_name;\n const localStorageCanBeSavedToServer = theUserIsLoggedIn && adventure.save_info === 'local-storage';\n const isUnchangeable = isServerSaveInfo(adventure.save_info) ? adventure.save_info.submitted : false;\n\n // Do not autosave the program if the size is very small compared to the previous\n // save. This protects against accidental `Ctrl-A, hit a key` and everything is gone. Clicking the\n // \"Run\" button will always save regardless of size.\n const wasSavedBefore = adventure.save_info !== undefined;\n const suspiciouslySmallFraction = 0.5;\n const programSuspiciouslyShrunk = wasSavedBefore && theGlobalEditor.contents.length < adventure.editor_contents.length * suspiciouslySmallFraction;\n\n return (programChanged || nameChanged || localStorageCanBeSavedToServer) && !isUnchangeable && !programSuspiciouslyShrunk;\n}\n\n/**\n * (Re)set a timer to trigger a save in N second\n */\nlet saveTimer: number | undefined;\nexport function triggerAutomaticSave() {\n const saveSeconds = 20;\n cancelPendingAutomaticSave();\n saveTimer = window.setTimeout(() => saveIfNecessary(), saveSeconds * 1000);\n}\n\nfunction cancelPendingAutomaticSave() {\n if (saveTimer) {\n window.clearTimeout(saveTimer);\n }\n}\n\n\nlet autoSaveEnabled = true;\n\nfunction disableAutomaticSaving() {\n autoSaveEnabled = false;\n}\n\nasync function saveIfNecessary() {\n if (!autoSaveEnabled) {\n return;\n }\n\n // Async-safe copy of current tab\n const adventureName = currentTab;\n const adventure = theAdventures[adventureName];\n if (!programNeedsSaving(adventureName) || !adventure) {\n return;\n }\n\n console.info('Saving program automatically...');\n\n const code = theGlobalEditor.contents;\n const saveName = saveNameFromInput();\n\n\n if (theUserIsLoggedIn) {\n const saveInfo = isServerSaveInfo(adventure.save_info) ? adventure.save_info : undefined;\n const response = await postJsonWithAchievements('/programs', {\n level: theLevel,\n lang: theLanguage,\n name: saveName,\n code: code,\n adventure_name: adventureName,\n program_id: saveInfo?.id,\n // We pass 'public' in here to save the backend a lookup\n share: saveInfo?.public,\n });\n\n // Record that we saved successfully\n adventure.editor_contents = code;\n if (response.save_info) {\n adventure.save_info = response.save_info;\n }\n localDelete(currentTabLsKey());\n } else {\n localSave(currentTabLsKey(), { saveName, code });\n adventure.editor_contents = code;\n }\n}\n\nfunction currentTabLsKey() {\n return `save-${currentTab}-${theLevel}`;\n}\n\nexport async function share_program(id: string, index: number, Public: boolean, prompt: string) {\n await modal.confirmP(prompt);\n await tryCatchPopup(async () => {\n const response = await postJsonWithAchievements('/programs/share', { id, public: Public });\n showAchievements(response.achievement, true, \"\");\n if (Public) {\n change_shared(true, index);\n } else {\n change_shared(false, index);\n }\n modal.notifySuccess(response.message);\n });\n}\n\nfunction change_shared (shared: boolean, index: number) {\n // Index is a front-end unique given to each program container and children\n // This value enables us to remove, hide or show specific element without connecting to the server (again)\n // When index is -1 we share the program from code page (there is no program container) -> no visual change needed\n if (index == -1) {\n return;\n }\n if (shared) {\n $('#non_public_button_container_' + index).hide();\n $('#public_button_container_' + index).show();\n $('#favourite_program_container_' + index).show();\n } else {\n $('#modal-copy-button').hide();\n $('#public_button_container_' + index).hide();\n $('#non_public_button_container_' + index).show();\n $('#favourite_program_container_' + index).hide();\n\n // In the theoretical situation that a user unshares their favourite program -> Change UI\n $('#favourite_program_container_' + index).removeClass('text-yellow-400');\n $('#favourite_program_container_' + index).addClass('text-white');\n }\n}\n\nexport function goToLevel(level: any) {\n const hash = window.location.hash\n let newPath = window.location.pathname.replace(/\\/\\d+/, `/${level}`);\n if (!newPath.includes(level)) {\n newPath = window.location.pathname + `/${level}`\n }\n window.location.pathname = newPath\n window.location.hash = hash\n}\n", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n function next() {\n while (env.stack.length) {\n var rec = env.stack.pop();\n try {\n var result = rec.dispose && rec.dispose.call(rec.value);\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n catch (e) {\n fail(e);\n }\n }\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "/**\n * An event emitter with the ability to cancel and defer events\n */\nexport class EventEmitter {\n private events: {[K in keyof Events]: EventHandlers};\n\n constructor(events: {[k in keyof Events]: true}) {\n this.events = Object.fromEntries(Object.keys(events).map(k => [k, []])) as any;\n }\n\n public on(name: E, handler: EventHandler) {\n this.events[name].push(handler);\n }\n\n public emit(name: E, args: Events[E]): Events[E] & Event {\n let status: 'sync' | 'canceled' | 'deferred' = 'sync';\n let _block: () => void;\n\n const ev: Events[E] & Event = {\n ...args,\n cancelEvent() { status = 'canceled'; },\n deferEvent() {\n status = 'deferred';\n return () => {\n if (_block) {\n _block();\n }\n };\n },\n then(block) {\n if (status === 'sync') {\n block();\n } else if (status === 'deferred') {\n _block = block;\n }\n },\n };\n\n for (const handler of this.events[name]) {\n handler(ev);\n }\n\n return ev;\n }\n}\n\nexport interface Event {\n /**\n * Cancel synchronous execution of the event\n */\n cancelEvent(): void;\n\n /**\n * Don't invoke the event code when 'then' is called, instead invoke it when the\n * returned function is called\n */\n deferEvent(): () => void;\n\n /**\n * Run the actual code if the event wasn't canceled, or will be invoked if it's deferred.\n */\n then(cb: () => void): void;\n}\n\nexport type EventHandler = (x: A & Event) => void;\nexport type EventHandlers = Array>;\n", "import { EventEmitter } from './event-emitter';\n\nexport interface SwitchTabsEvent {\n readonly oldTab: string;\n readonly newTab: string;\n}\n\nexport interface TabEvents {\n beforeSwitch: SwitchTabsEvent;\n afterSwitch: SwitchTabsEvent;\n}\n\nexport interface TabOptions {\n readonly initialTab?: string;\n}\n\n/**\n * Tabs\n *\n * Protocol:\n *\n * - Tabs consist of a TAB (the sticky-outy bit) and a TARGET\n * (the pane that gets shown and hidden).\n *\n * - TABS should have: <... data-tab=\"SOME-ID\">\n *\n * - TARGETS should have: <... data-tabtarget=\"SOME-ID\">\n *\n * When a TAB is clicked, the TARGET with the matching id is shown\n * (and all other TARGETS in the same containing HTML element are hidden).\n *\n * The active TAB is indicated by the '.tab-selected' class, the active\n * TARGET by the *absence* of the '.hidden' class.\n */\nexport class Tabs {\n private _currentTab: string = '';\n\n private tabEvents = new EventEmitter({\n beforeSwitch: true,\n afterSwitch: true,\n });\n\n constructor(options: TabOptions={}) {\n $('*[data-tab]').on('click', (e) => {\n const tab = $(e.target);\n const tabName = tab.data('tab') as string;\n\n e.preventDefault();\n this.switchToTab(tabName);\n });\n\n // Determine initial tab\n // 1. Given by code\n // 2. In the URL\n // 3. Otherwise the first one we find\n let initialTab = options.initialTab;\n if (!initialTab && window.location.hash) {\n const hashFragment = window.location.hash.replace(/^#/, '');\n initialTab = hashFragment;\n }\n if (!initialTab) {\n initialTab = $('.tab:first').attr('data-tab');\n }\n\n if (initialTab) {\n this.switchToTab(initialTab);\n }\n }\n\n public switchToTab(tabName: string) {\n const doSwitch = () => {\n const oldTab = this._currentTab;\n this._currentTab = tabName;\n\n // Do a 'replaceState' to add a '#anchor' to the URL\n const hashFragment = tabName !== 'level' ? tabName : '';\n if (window.history) { window.history.replaceState(null, '', '#' + hashFragment); }\n\n // Find the tab that leads to this selection, and its siblings\n const tab = $('*[data-tab=\"' + tabName + '\"]');\n const allTabs = tab.siblings('*[data-tab]');\n\n // Find the target associated with this selection, and its siblings\n const target = $('*[data-tabtarget=\"' + tabName + '\"]');\n const allTargets = target.siblings('*[data-tabtarget]');\n\n // Fix classes\n allTabs.removeClass('tab-selected');\n tab.addClass('tab-selected');\n\n allTargets.addClass('hidden');\n target.removeClass('hidden');\n\n this.tabEvents.emit('afterSwitch', { oldTab, newTab: tabName });\n }\n\n // We don't do a beforeSwitch event for the very first tab switch\n if (this._currentTab != '') {\n const event = this.tabEvents.emit('beforeSwitch', { oldTab: this._currentTab, newTab: tabName });\n event.then(doSwitch);\n } else {\n doSwitch();\n }\n }\n\n public get currentTab() {\n return this._currentTab;\n }\n\n public on(key: Parameters[0], handler: Parameters[1]) {\n const ret = this.tabEvents.on(key, handler);\n // Immediately invoke afterSwitch when it's being registered\n if (key === 'afterSwitch') {\n this.tabEvents.emit('afterSwitch', { oldTab: '', newTab: this._currentTab });\n }\n return ret;\n }\n}\n\n\nexport function getPreviousAndNext() {\n const selected = document.querySelector('.tab-selected')\n if (!selected) return []\n const i = parseInt(selected.getAttribute('tabindex') || '0')\n const prev = document.querySelector(`.tab[tabindex='${i-1}']`)\n const next = document.querySelector(`.tab[tabindex='${i+1}']`)\n return [prev, next]\n}", "/*******************************************************************************\n*\n* !!! THIS FILE HAS BEEN GENERATED. DO NOT EDIT !!!\n*\n* Make desired changes to prefixes/{normal,pygame,turtle,music}.py\n*\n* Add/edit tests for those changes in tests/test_python_prefixes.py\n*\n* Run build-tools/heroku/generate-prefixes-ts to regenerate.\n*\n*******************************************************************************/\n\nexport const turtle_prefix = \n`# coding=utf8\n\nimport random # noqa F401\nimport time # noqa F401\nimport turtle\n\nt = turtle.Turtle()\nt.shape(\"turtle\")\nt.hideturtle()\nt.penup()\nt.left(90)\nt.pendown()\nt.speed(3)\nt.showturtle()\n`;\n\nexport const pygame_prefix = \n`# coding=utf8\n\nimport pygame # noqa F401\nimport buttons # noqa F401\n\npygame.init()\ncanvas = pygame.display.set_mode((711, 300))\ncanvas.fill(pygame.Color(247, 250, 252, 255))\n\npygame_end = False\nbutton_list = []\n\n\ndef create_button(name):\n if name not in button_list:\n button_list.append(name)\n buttons.add(name)\n`;\n\nexport const normal_prefix = \n`# coding=utf8\n\nimport random # noqa F401\nimport time # noqa F401\n\ntry:\n import extensions # noqa F401\nexcept ModuleNotFoundError:\n # This is done because 'extensions' is not a python module but rather a Skulpt JS extension\n # These functions are defined in skulpt-stdlib-extensions.js\n # When running tests in test_python_prefixes it wil raise ModuleNotFoundError\n pass\n\nglobal int_saver\nglobal convert_numerals # needed for recursion to work\nint_saver = int\n\n\ndef int(s):\n if isinstance(s, str):\n numerals_dict = {'0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8',\n '9': '9', '\uD804\uDC66': '0', '\uD804\uDC67': '1', '\uD804\uDC68': '2', '\uD804\uDC69': '3', '\uD804\uDC6A': '4', '\uD804\uDC6B': '5', '\uD804\uDC6C': '6', '\uD804\uDC6D': '7',\n '\uD804\uDC6E': '8', '\uD804\uDC6F': '9', '\u0966': '0', '\u0967': '1', '\u0968': '2', '\u0969': '3', '\u096A': '4', '\u096B': '5', '\u096C': '6',\n '\u096D': '7', '\u096E': '8', '\u096F': '9', '\u0AE6': '0', '\u0AE7': '1', '\u0AE8': '2', '\u0AE9': '3', '\u0AEA': '4', '\u0AEB': '5',\n '\u0AEC': '6', '\u0AED': '7', '\u0AEE': '8', '\u0AEF': '9', '\u0A66': '0', '\u0A67': '1', '\u0A68': '2', '\u0A69': '3', '\u0A6A': '4',\n '\u0A6B': '5', '\u0A6C': '6', '\u0A6D': '7', '\u0A6E': '8', '\u0A6F': '9', '\u09E6': '0', '\u09E7': '1', '\u09E8': '2', '\u09E9': '3',\n '\u09EA': '4', '\u09EB': '5', '\u09EC': '6', '\u09ED': '7', '\u09EE': '8', '\u09EF': '9', '\u0CE6': '0', '\u0CE7': '1', '\u0CE8': '2',\n '\u0CE9': '3', '\u0CEA': '4', '\u0CEB': '5', '\u0CEC': '6', '\u0CED': '7', '\u0CEE': '8', '\u0CEF': '9', '\u0B66': '0', '\u0B67': '1',\n '\u0B68': '2', '\u0B69': '3', '\u0B6A': '4', '\u0B6B': '5', '\u0B6C': '6', '\u0B6D': '7', '\u0B6E': '8', '\u0B6F': '9', '\u0D66': '0',\n '\u0D67': '1', '\u0D68': '2', '\u0D69': '3', '\u0D6A': '4', '\u0D6B': '5', '\u0D6C': '6', '\u0D6D': '7', '\u0D6E': '8', '\u0D6F': '9',\n '\u0BE6': '0', '\u0BE7': '1', '\u0BE8': '2', '\u0BE9': '3', '\u0BEA': '4', '\u0BEB': '5', '\u0BEC': '6', '\u0BED': '7', '\u0BEE': '8',\n '\u0BEF': '9', '\u0C66': '0', '\u0C67': '1', '\u0C68': '2', '\u0C69': '3', '\u0C6A': '4', '\u0C6B': '5', '\u0C6C': '6', '\u0C6D': '7',\n '\u0C6E': '8', '\u0C6F': '9', '\u1040': '0', '\u1041': '1', '\u1042': '2', '\u1043': '3', '\u1044': '4', '\u1045': '5', '\u1046': '6',\n '\u1047': '7', '\u1048': '8', '\u1049': '9', '\u0F20': '0', '\u0F21': '1', '\u0F22': '2', '\u0F23': '3', '\u0F24': '4', '\u0F25': '5',\n '\u0F26': '6', '\u0F27': '7', '\u0F28': '8', '\u0F29': '9', '\u1810': '0', '\u1811': '1', '\u1812': '2', '\u1813': '3', '\u1814': '4',\n '\u1815': '5', '\u1816': '6', '\u1817': '7', '\u1818': '8', '\u1819': '9', '\u17E0': '0', '\u17E1': '1', '\u17E2': '2', '\u17E3': '3',\n '\u17E4': '4', '\u17E5': '5', '\u17E6': '6', '\u17E7': '7', '\u17E8': '8', '\u17E9': '9', '\u0E50': '0', '\u0E51': '1', '\u0E52': '2',\n '\u0E53': '3', '\u0E54': '4', '\u0E55': '5', '\u0E56': '6', '\u0E57': '7', '\u0E58': '8', '\u0E59': '9', '\u0ED0': '0', '\u0ED1': '1',\n '\u0ED2': '2', '\u0ED3': '3', '\u0ED4': '4', '\u0ED5': '5', '\u0ED6': '6', '\u0ED7': '7', '\u0ED8': '8', '\u0ED9': '9', '\uA9D0': '0',\n '\uA9D1': '1', '\uA9D2': '2', '\uA9D3': '3', '\uA9D4': '4', '\uA9D5': '5', '\uA9D6': '6', '\uA9D7': '7', '\uA9D8': '8', '\uA9D9': '9',\n '\u0660': '0', '\u0661': '1', '\u0662': '2', '\u0663': '3', '\u0664': '4', '\u0665': '5', '\u0666': '6', '\u0667': '7', '\u0668': '8',\n '\u0669': '9', '\u06F0': '0', '\u06F1': '1', '\u06F2': '2', '\u06F3': '3', '\u06F4': '4', '\u06F5': '5', '\u06F6': '6', '\u06F7': '7',\n '\u06F8': '8', '\u06F9': '9', '\u3007': '0', '\u4E00': '1', '\u4E8C': '2', '\u4E09': '3', '\u56DB': '4', '\u4E94': '5', '\u516D': '6',\n '\u4E03': '7', '\u516B': '8', '\u4E5D': '9', '\u96F6': '0'}\n latin_numerals = ''.join([numerals_dict.get(letter, letter) for letter in s])\n return int_saver(latin_numerals)\n return (int_saver(s))\n\n\ndef convert_numerals(alphabet, number):\n numerals_dict_return = {\n 'Latin': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],\n 'Brahmi': ['\uD804\uDC66', '\uD804\uDC67', '\uD804\uDC68', '\uD804\uDC69', '\uD804\uDC6A', '\uD804\uDC6B', '\uD804\uDC6C', '\uD804\uDC6D', '\uD804\uDC6E', '\uD804\uDC6F'],\n 'Devanagari': ['\u0966', '\u0967', '\u0968', '\u0969', '\u096A', '\u096B', '\u096C', '\u096D', '\u096E', '\u096F'],\n 'Gujarati': ['\u0AE6', '\u0AE7', '\u0AE8', '\u0AE9', '\u0AEA', '\u0AEB', '\u0AEC', '\u0AED', '\u0AEE', '\u0AEF'],\n 'Gurmukhi': ['\u0A66', '\u0A67', '\u0A68', '\u0A69', '\u0A6A', '\u0A6B', '\u0A6C', '\u0A6D', '\u0A6E', '\u0A6F'],\n 'Bengali': ['\u09E6', '\u09E7', '\u09E8', '\u09E9', '\u09EA', '\u09EB', '\u09EC', '\u09ED', '\u09EE', '\u09EF'],\n 'Kannada': ['\u0CE6', '\u0CE7', '\u0CE8', '\u0CE9', '\u0CEA', '\u0CEB', '\u0CEC', '\u0CED', '\u0CEE', '\u0CEF'],\n 'Odia': ['\u0B66', '\u0B67', '\u0B68', '\u0B69', '\u0B6A', '\u0B6B', '\u0B6C', '\u0B6D', '\u0B6E', '\u0B6F'],\n 'Malayalam': ['\u0D66', '\u0D67', '\u0D68', '\u0D69', '\u0D6A', '\u0D6B', '\u0D6C', '\u0D6D', '\u0D6E', '\u0D6F'],\n 'Tamil': ['\u0BE6', '\u0BE7', '\u0BE8', '\u0BE9', '\u0BEA', '\u0BEB', '\u0BEC', '\u0BED', '\u0BEE', '\u0BEF'],\n 'Telugu': ['\u0C66', '\u0C67', '\u0C68', '\u0C69', '\u0C6A', '\u0C6B', '\u0C6C', '\u0C6D', '\u0C6E', '\u0C6F'],\n 'Burmese': ['\u1040', '\u1041', '\u1042', '\u1043', '\u1044', '\u1045', '\u1046', '\u1047', '\u1048', '\u1049'],\n 'Tibetan': ['\u0F20', '\u0F21', '\u0F22', '\u0F23', '\u0F24', '\u0F25', '\u0F26', '\u0F27', '\u0F28', '\u0F29'],\n 'Mongolian': ['\u1810', '\u1811', '\u1812', '\u1813', '\u1814', '\u1815', '\u1816', '\u1817', '\u1818', '\u1819'],\n 'Khmer': ['\u17E0', '\u17E1', '\u17E2', '\u17E3', '\u17E4', '\u17E5', '\u17E6', '\u17E7', '\u17E8', '\u17E9'],\n 'Thai': ['\u0E50', '\u0E51', '\u0E52', '\u0E53', '\u0E54', '\u0E55', '\u0E56', '\u0E57', '\u0E58', '\u0E59'],\n 'Lao': ['\u0ED0', '\u0ED1', '\u0ED2', '\u0ED3', '\u0ED4', '\u0ED5', '\u0ED6', '\u0ED7', '\u0ED8', '\u0ED9'],\n 'Javanese': ['\uA9D0', '\uA9D1', '\uA9D2', '\uA9D3', '\uA9D4', '\uA9D5', '\uA9D6', '\uA9D7', '\uA9D8', '\uA9D9'],\n 'Arabic': ['\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668', '\u0669'],\n 'Persian': ['\u06F0', '\u06F1', '\u06F2', '\u06F3', '\u06F4', '\u06F5', '\u06F6', '\u06F7', '\u06F8', '\u06F9'],\n 'Urdu': ['\u06F0', '\u06F1', '\u06F2', '\u06F3', '\u06F4', '\u06F5', '\u06F6', '\u06F7', '\u06F8', '\u06F9']\n }\n\n number = str(number)\n T = str\n\n sign = ''\n if number[0] == '-':\n sign = '-'\n number = number[1:]\n\n if number.replace('.', '', 1).isnumeric():\n numerals_list = numerals_dict_return[alphabet]\n if '.' in number:\n tokens = number.split('.')\n all_numerals_converted = [numerals_list[int(digit)] for digit in tokens[0]]\n all_numerals_converted.append('.')\n all_numerals_converted.extend(numerals_list[int(digit)] for digit in tokens[1])\n if alphabet == 'Latin':\n T = float\n else:\n all_numerals_converted = [numerals_list[int(digit)] for digit in number]\n if alphabet == 'Latin':\n T = int\n number = ''.join(all_numerals_converted)\n return T(f'{sign}{number}')\n`;\n\nexport const music_prefix = \n`\nnotes_mapping = {\n 'C': 'C4',\n 'D': 'D4',\n 'E': 'E4',\n 'F': 'F4',\n 'G': 'G4',\n 'A': 'A4',\n 'B': 'B4',\n '1': 'C0',\n '2': 'D0',\n '3': 'E0',\n '4': 'F0',\n '5': 'G0',\n '6': 'A0',\n '7': 'B0',\n '8': 'C1',\n '9': 'D1',\n '10': 'E1',\n '11': 'F1',\n '12': 'G1',\n '13': 'A1',\n '14': 'B1',\n '15': 'C2',\n '16': 'D2',\n '17': 'E2',\n '18': 'F2',\n '19': 'G2',\n '20': 'A2',\n '21': 'B2',\n '22': 'C3',\n '23': 'D3',\n '24': 'E3',\n '25': 'F3',\n '26': 'G3',\n '27': 'A3',\n '28': 'B3',\n '29': 'C4',\n '30': 'D4',\n '31': 'E4',\n '32': 'F4',\n '33': 'G4',\n '34': 'A4',\n '35': 'B4',\n '36': 'C5',\n '37': 'D5',\n '38': 'E5',\n '39': 'F5',\n '40': 'G5',\n '41': 'A5',\n '42': 'B5',\n '43': 'C6',\n '44': 'D6',\n '45': 'E6',\n '46': 'F6',\n '47': 'G6',\n '48': 'A6',\n '49': 'B6',\n '50': 'C7',\n '51': 'D7',\n '52': 'E7',\n '53': 'F7',\n '54': 'G7',\n '55': 'A7',\n '56': 'B7',\n '57': 'C8',\n '58': 'D8',\n '59': 'E8',\n '60': 'F8',\n '61': 'G8',\n '62': 'A8',\n '63': 'B8',\n '64': 'C9',\n '65': 'D9',\n '66': 'E9',\n '67': 'F9',\n '68': 'G9',\n '69': 'A9',\n '70': 'B9',\n}\n`;\n", "export interface Adventure {\n /** Identifier */\n short_name: string;\n\n /** Translated tab name */\n name: string;\n\n /** The type of adventure */\n is_teacher_adventure: boolean;\n\n /** Is this an adventure about a keyword? */\n is_command_adventure: boolean;\n\n /** Markdown for the introductory paragraph */\n text?: string;\n\n /** Example code for the introductory paragraph */\n example_code?: string;\n\n /** Image to go along with the tab (unused) */\n image?: string | null;\n\n /** More paragraphs of text and code */\n extra_stories?: ExtraStory[];\n\n /**\n * Name of the saved program for this adventure\n *\n * (Either the loaded program or a default).\n */\n save_name: string;\n\n /**\n * Initial code given in the editor\n *\n * (Either from the loaded program or a default).\n */\n editor_contents: string;\n\n /**\n * If the current program is from a save, some additional\n * information about the save.\n *\n * Either a ServerSaveInfo object if the program was loaded from a server save,\n * or the string 'local-storage' if the program was loaded from local storage.\n */\n save_info?: ServerSaveInfo | 'local-storage';\n}\n\n/**\n * Whether the given field is actually a SaveInfo object\n */\nexport function isServerSaveInfo(x: Adventure['save_info']): x is ServerSaveInfo {\n return !!x && typeof x === 'object' && !!x.id;\n}\n\nexport interface ServerSaveInfo {\n /** Identifier of the program in the database */\n id: string;\n\n /**\n * Public is 0, 1 or undefined\n */\n public?: number;\n\n /**\n * Submitted is false, true or undefined\n */\n submitted?: boolean;\n\n /**\n * URL for this program if it is public\n */\n public_url?: string;\n}\n\nexport interface ExtraStory {\n example_code?: string;\n text?: string;\n}\n\nexport interface Program {\n name: string;\n code: string;\n adventure_name?: string;\n}\n\n/**\n * Definition of an achievement\n *\n * Array of [title, text, statistics].\n */\nexport type Achievement = [string, string, string];\n", "// All these functions are general and easy to use within all tutorial .ts files\n// Most important is the tutorialPopup() function that retrieves the actual step from the server\n\nimport {modal} from \"../modal\";\n\nexport function addHighlightBorder(element_id: string) {\n $('#' + element_id).addClass('border-2 rounded-lg border-red-500');\n}\n\nexport function removeBorder(element_id: string) {\n $('#' + element_id).removeClass('border-2 border-red-500');\n}\n\nexport function relocatePopup(x: number, y: number) {\n $('#tutorial-pop-up').css({'top': '20%', 'left': '50%'});\n if (x && y) {\n let left = x.toString() + \"%\"\n let top = y.toString() + \"%\"\n $('#tutorial-pop-up').css({'top': top, 'left': left});\n }\n\n}\n\nexport function tutorialPopup(current_level: string, step: number) {\n let route = \"/get_tutorial_step/\" + current_level + \"/\"\n $.ajax({\n type: 'GET',\n url: route + step.toString(),\n dataType: 'json'\n }).done(function(response: any) {\n $('#tutorial_title').text(response.title);\n $('#tutorial_text').text(response.text);\n $('#tutorial-pop-up').fadeIn(800);\n }).fail(function(response) {\n modal.notifyError(response.responseText);\n });\n}", "import { ClientMessages } from '../client-messages';\n\nfunction unloadHandler(event: BeforeUnloadEvent) {\n event.preventDefault();\n return event.returnValue = ClientMessages['Unsaved_Changes'];\n}\n\nlet unsavedChanges = false;\n\nexport function hasUnsavedChanges() {\n return unsavedChanges;\n}\n\n/**\n * Whether there are unsaved changes (used by the HTML)\n */\nexport function markUnsavedChanges() {\n unsavedChanges = true;\n window.addEventListener('beforeunload', unloadHandler, { capture: true });\n}\n\n/**\n * Clear unsaved changes marker\n */\nexport function clearUnsavedChanges() {\n unsavedChanges = false;\n // MDN tells me to add and remove this listener as necessary: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event\n window.removeEventListener('beforeunload', unloadHandler, { capture: true });\n}\n", "import {pushAchievement, runit, theGlobalEditor} from \"../app\";\nimport {addHighlightBorder, relocatePopup, removeBorder, tutorialPopup} from \"./utils\";\nimport { clearUnsavedChanges } from '../browser-helpers/unsaved-changes';\n\nlet current_step = 0;\n\nexport function startIntro() {\n current_step = 1;\n $('#adventures').hide();\n $('#variables_container').hide();\n theGlobalEditor.contents = \"\";\n\n tutorialPopup(\"intro\", current_step);\n}\n\nexport function callNextIntroStep() {\n current_step += 1;\n\n if (current_step == 2) {\n codeEditorStep();\n } else if (current_step == 3) {\n codeOutputStep();\n } else if (current_step == 4) {\n runButtonStep();\n } else if (current_step == 5) {\n tryRunButtonStep();\n } else if (current_step == 6) {\n speakAloudStep();\n } else if (current_step == 7) {\n runSpeakAloudStep();\n } else if (current_step == 8) {\n nextLevelStep();\n } else if (current_step == 9) {\n levelDefaultStep();\n } else if (current_step == 10) {\n adventureTabsStep();\n } else if (current_step == 11) {\n parsonsTabStep();\n } else if (current_step == 12) {\n quizTabStep();\n } else if (current_step == 13) {\n saveShareStep();\n } else if (current_step == 14) {\n cheatsheetStep();\n } else if (current_step == 15) {\n pushAchievement(\"well_begun_is_half_done\");\n $('#achievement_pop-up').removeClass('z-10');\n $('#achievement_pop-up').addClass('z-50');\n // If the achievement pop-up is visible -> wait with the next function call\n setTimeout(function(){\n if ($('#achievement_pop-up').is(':visible')) {\n setTimeout(function() {\n endTutorial();\n $('#achievement_pop-up').removeClass('z-50');\n $('#achievement_pop-up').addClass('z-10');\n }, 5000);\n } else {\n endTutorial();\n $('#achievement_pop-up').removeClass('z-50');\n $('#achievement_pop-up').addClass('z-10');\n }\n }, 500);\n } else {\n location.replace(\"/hedy\");\n }\n}\n\nfunction codeEditorStep() {\n $('#editor').addClass(\"z-40\");\n addHighlightBorder(\"editor\");\n\n relocatePopup(65, 30);\n theGlobalEditor.contents = \"print ___\"; \n tutorialPopup(\"intro\", current_step);\n}\n\nfunction codeOutputStep() {\n removeBorder(\"editor\");\n $('#code_output').addClass(\"z-40\");\n addHighlightBorder(\"code_output\");\n\n runit (1, \"en\", \"\", \"run\",function () {\n $ ('#output').focus ();\n });\n\n relocatePopup(35, 30);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction runButtonStep() {\n removeBorder(\"code_output\");\n $('#code_related_buttons').show();\n $('#runButtonContainer').addClass(\"z-40\");\n addHighlightBorder(\"runButtonContainer\");\n\n relocatePopup(50, 30);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction tryRunButtonStep() {\n $.ajax({\n type: 'GET',\n url: '/get_tutorial_step/intro/code_snippet/',\n dataType: 'json'\n }).done(function(response: any) {\n theGlobalEditor.contents = response.code;\n }).fail(function() {\n theGlobalEditor.contents = \"print Hello world!\\nprint I'm learning Hedy with the tutorial!\";\n });\n\n relocatePopup(50, 70);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction speakAloudStep() {\n removeBorder(\"runButtonContainer\");\n $('#editor').removeClass('z-40');\n $('#code_output').removeClass('z-40');\n $('#runButtonContainer').removeClass('z-40');\n\n $('#speak_container').addClass('z-40 bg-white relative');\n\n addHighlightBorder(\"speak_container\");\n\n relocatePopup(50, 30);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction runSpeakAloudStep() {\n $('#editor').addClass('z-40');\n $('#code_output').addClass('z-40');\n $('#runButtonContainer').addClass('z-40');\n\n relocatePopup(50, 70);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction nextLevelStep() {\n removeBorder(\"speak_container\");\n $('#editor').removeClass('z-40');\n $('#code_output').removeClass('z-40');\n $('#runButtonContainer').removeClass('z-40');\n $('#speak_container').removeClass('z-40 bg-white relative');\n\n $('#next_level_button').addClass(\"z-40\");\n $('#next_level_button').removeAttr('onclick');\n addHighlightBorder(\"next_level_button\");\n\n relocatePopup(50, 30);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction levelDefaultStep() {\n removeBorder(\"next_level_button\");\n $('#next_level_button').removeClass('z-40');\n\n $('#code_content_container').addClass('z-40');\n $('#adventures').addClass('z-40 bg-gray-100');\n $('#adventures').show();\n\n // Set to false, prevent \"are you sure you want to switch without saving\" pop-up\n clearUnsavedChanges();\n\n addHighlightBorder(\"adventures\");\n relocatePopup(50, 40);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction adventureTabsStep() {\n $('#adventures-buttons').children().each(function() {\n if ($(this).attr('data-tab') == \"story\") {\n // Set to false, prevent \"are you sure you want to switch without saving\" pop-up\n clearUnsavedChanges();\n $(this).click();\n }\n });\n\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction parsonsTabStep() {\n $('#adventures-buttons').children().each(function() {\n if ($(this).attr('data-tab') == \"parsons\") {\n // Set to false, prevent \"are you sure you want to switch without saving\" pop-up\n clearUnsavedChanges();\n $(this).click();\n }\n });\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction quizTabStep() {\n $('#adventures-buttons').children().each(function() {\n if ($(this).attr('data-tab') == \"quiz\") {\n // Set to false, prevent \"are you sure you want to switch without saving\" pop-up\n clearUnsavedChanges();\n $(this).click();\n }\n });\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction saveShareStep() {\n // We should go back to the intro tab to make sure the save/share option is shown\n $('#adventures-buttons').children().each(function() {\n if ($(this).attr('data-tab') == \"default\") {\n clearUnsavedChanges();\n $(this).click();\n }\n });\n removeBorder(\"adventures\");\n $('#code_content_container').removeClass('z-40');\n $('#level-header').addClass(\"z-40\");\n $('#cheatsheet_container').hide();\n addHighlightBorder(\"level-header\");\n\n $('#save_program_button').removeAttr('onclick');\n $('#share_program_button').removeAttr('onclick');\n\n relocatePopup(50, 30);\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction cheatsheetStep() {\n $('#cheatsheet_container').show();\n $('#code_output').removeClass('z-40');\n $('#adventures').removeClass('z-40');\n $('#cheatsheet_dropdown').addClass('z-40');\n $('#cheatsheet_dropdown').show();\n\n tutorialPopup(\"intro\", current_step);\n}\n\nfunction endTutorial() {\n removeBorder(\"level-header\");\n $('#level-header').removeClass('z-40');\n $('#cheatsheet_dropdown').removeClass('z-40');\n $('#cheatsheet_dropdown').hide();\n\n relocatePopup(50, 15);\n tutorialPopup(\"intro\", current_step);\n}\n\n", "import {pushAchievement} from \"../app\";\nimport {addHighlightBorder, relocatePopup, removeBorder, tutorialPopup} from \"./utils\";\n\nlet current_step = 0;\n\nexport function startTeacher() {\n current_step = 1;\n\n tutorialPopup(\"teacher\", current_step);\n}\n\nexport function callTeacherNextStep() {\n current_step += 1;\n if (current_step == 2) {\n classStep();\n } else if (current_step == 3) {\n customizeClassStep();\n } else if (current_step == 4) {\n adventureStep();\n } else if (current_step == 5) {\n multipleAccountsStep();\n } else if (current_step == 6) {\n documentationStep();\n } else if (current_step == 7) {\n pushAchievement(\"ring_the_bell\");\n $('#achievement_pop-up').removeClass('z-10');\n $('#achievement_pop-up').addClass('z-50');\n // If the achievement pop-up is visible -> wait with the next function call\n setTimeout(function(){\n if ($('#achievement_pop-up').is(':visible')) {\n setTimeout(function() {\n teacherEndStep();\n $('#achievement_pop-up').removeClass('z-50');\n $('#achievement_pop-up').addClass('z-10');\n }, 5000);\n } else {\n teacherEndStep();\n $('#achievement_pop-up').removeClass('z-50');\n $('#achievement_pop-up').addClass('z-10');\n }\n }, 500);\n } else {\n location.replace(\"/for-teachers\");\n }\n}\n\nfunction classStep() {\n $('#auth_main_container').addClass('z-40');\n $('#teacher_classes').addClass('z-40 bg-gray-100');\n addHighlightBorder(\"teacher_classes\");\n\n relocatePopup(50, 40);\n tutorialPopup(\"teacher\", current_step);\n}\n\nfunction customizeClassStep() {\n tutorialPopup(\"teacher\", current_step);\n}\n\nfunction adventureStep() {\n $('#teacher_adventures').addClass('z-40 bg-gray-100');\n removeBorder(\"teacher_classes\");\n addHighlightBorder(\"teacher_adventures\");\n\n relocatePopup(50, 70);\n tutorialPopup(\"teacher\", current_step);\n}\n\nfunction multipleAccountsStep() {\n $('#teacher_accounts').addClass('z-40 bg-gray-100');\n removeBorder(\"teacher_adventures\");\n addHighlightBorder(\"teacher_accounts\");\n\n relocatePopup(50, 20);\n tutorialPopup(\"teacher\", current_step);\n}\n\nfunction documentationStep() {\n $('#teacher_documentation').addClass('z-40 bg-gray-100');\n removeBorder(\"teacher_accounts\");\n addHighlightBorder(\"teacher_documentation\")\n\n tutorialPopup(\"teacher\", current_step);\n}\n\nfunction teacherEndStep() {\n removeBorder(\"teacher_documentation\");\n tutorialPopup(\"teacher\", current_step);\n}", "/*** ADDING A TUTORIAL LEVEL SHORT GUIDE ***/\n\n// Use this file as a template to create the tutorial for any level\n// Create a dedicated file such as \"level2.ts\" and make sure you have startLevelX() and callNextStepLevelX() functions\n// Add these to the startLevel() and callNextLevelStep() functions in tutorial.ts and make sure to import them correctly\n// Also make sure that for every step/level combination there is an entry in the corresponding YAML file\n// To start the tutorial for a specific level, for example level 1\n// Call \"startLevelTutorial()\" on from a template, the rest should be handled automatically\n\nimport {theGlobalEditor} from \"../app\";\nimport {tutorialPopup, relocatePopup} from \"./utils\";\n\nlet current_step = 0;\n\nexport function startLevel1() {\n current_step = 1;\n\n tutorialPopup(\"1\", current_step);\n}\n\nexport function callNextStepLevel1() {\n current_step += 1;\n\n if (current_step == 2) {\n theGlobalEditor.contents = \"print Hello world!\";\n relocatePopup(50, 70);\n tutorialPopup(\"1\", 2);\n\n } else if (current_step == 3) {\n location.replace(\"/hedy\");\n }\n\n}", "import {callNextIntroStep, startIntro } from \"./intro\";\nimport {callTeacherNextStep, startTeacher} from \"./teacher\";\n\n// We should add this import for every level we add to the tutorial\nimport {callNextStepLevel1, startLevel1} from \"./level1\";\n\nlet current_level = \"\";\n\n// We call this function on load -> customize click event of the tutorial button\nexport function initializeTutorial() {\n $('#tutorial_next_button').off('click').on('click', () => {\n $('#tutorial-pop-up').hide();\n // If we are a student -> call the next student tutorial step, otherwise call the teacher step\n if (current_level == \"intro\") {\n return callNextIntroStep();\n } else if (current_level == \"teacher\") {\n return callTeacherNextStep();\n }\n return callNextLevelStep(current_level);\n });\n}\n\nexport function startIntroTutorial() {\n $('#tutorial-mask').show();\n current_level = \"intro\";\n startIntro();\n}\n\nexport function startLevelTutorial(level: string) {\n $('#tutorial-mask').show();\n current_level = level;\n startLevel(level);\n}\n\nexport function startTeacherTutorial() {\n $('#tutorial-mask').show();\n current_level = \"teacher\";\n startTeacher();\n}\n\nfunction startLevel(current_level: string) {\n if (current_level == \"1\") {\n startLevel1();\n }\n}\n\nfunction callNextLevelStep(current_level: string) {\n if (current_level == \"1\") {\n callNextStepLevel1();\n }\n}\n\n", "import { EventEmitter } from \"./event-emitter\";\nexport type Breakpoints = Record;\nexport enum EditorType {\n MAIN,\n MODAL,\n COMMON_MISTAKES,\n CHEATSHEET,\n PARSONS,\n EXAMPLE\n}\n\ntype EditorEventEmitter = EventEmitter;\ntype OnEditorEventParameters = Parameters;\n\nexport interface SourceRange {\n readonly startLine: number;\n readonly startColumn: number;\n readonly endLine: number;\n readonly endColumn: number;\n}\n\nexport interface EditorEvent {\n readonly change: string;\n readonly guttermousedown: string;\n readonly changeBreakpoint: string;\n readonly click: string\n}\n\nexport interface HedyEditorCreator {\n /**\n * This function should initialize the editor and set up all the required\n * event handlers\n * @param {JQuery} $editor reference to the div that contains the main editor\n * @param {EditorType} editorType the type of the editor\n */\n initializeEditorWithGutter($editor: JQuery, editorType: EditorType, dir?: string): HedyEditor;\n\n /**\n * Initializes a read only editor\n *\n * @param {HTMLElement} preview - The element to preview the editor.\n * @return {HedyEditor} The initialized Hedy editor instance.\n */\n initializeReadOnlyEditor(preview: HTMLElement, dir?: string): HedyEditor;\n}\n\nexport interface HedyEditor {\n /**\n * The contents of the editor\n */\n contents: string;\n /**\n * if the editor is set to read-only mode\n */\n isReadOnly: boolean;\n /**\n * Set the highlither rules for a particular level\n * @param level\n */\n setHighlighterForLevel(level: number): void;\n\n /**\n * Resizes the editor after changing its size programatically\n * @param newHeight the new height of the editor in rem, if supplied\n */\n resize(newHeight?: number): void;\n\n /**\n * Focuses the text area for the current editor\n */\n focus(): void;\n\n /**\n * Clears the errors and annotations in the editor\n */\n clearErrors(): void;\n\n /**\n * Moves to the cursor to the end of the current file\n */\n moveCursorToEndOfFile(): void;\n\n /**\n * Clears the selected text\n */\n clearSelection(): void;\n\n /**\n * Removes all breakpoints on the rows.\n **/\n clearBreakpoints(): void;\n\n /**\n * An event handler for the HedyEditor\n * @param key the event\n * @param handler the event handler function\n */\n\n on(key: OnEditorEventParameters[0], handler: any): void;\n\n /**\n * Trim trailing whitespaces\n */\n trimTrailingSpace: () => void;\n\n /**\n * Mark an error location in the editor\n *\n * The error occurs at the given row, and optionally has a column and\n * and a length.\n *\n * If 'col' is not given, the entire line will be highlighted red. Otherwise\n * the character at 'col' will be highlighted, optionally extending for\n * 'length' characters.\n *\n * 'row' and 'col' are 1-based.\n */\n highlightError(row: number, col?: number): void;\n\n /**\n * set incorrect line\n */\n setIncorrectLine(range: SourceRange, lineIndex: number): void;\n\n /**\n * Remove all incorrect lines markers\n */\n clearIncorrectLines(): void;\n\n /**\n * Set the current line in the debugger\n */\n setDebuggerCurrentLine(line?: number, startPos?: number, finishPos?: number): void;\n\n /**\n * Mark the given set of lines as currently struck through\n */\n getActiveContents(debugLine?: string | null): string;\n\n /**\n * Skip faulty event handler\n */\n skipFaultyHandler(event?: MouseEvent): void;\n}\n", "/**\nThe data structure for documents. @nonabstract\n*/\nclass Text {\n /**\n Get the line description around the given position.\n */\n lineAt(pos) {\n if (pos < 0 || pos > this.length)\n throw new RangeError(`Invalid position ${pos} in document of length ${this.length}`);\n return this.lineInner(pos, false, 1, 0);\n }\n /**\n Get the description for the given (1-based) line number.\n */\n line(n) {\n if (n < 1 || n > this.lines)\n throw new RangeError(`Invalid line number ${n} in ${this.lines}-line document`);\n return this.lineInner(n, true, 1, 0);\n }\n /**\n Replace a range of the text with the given content.\n */\n replace(from, to, text) {\n [from, to] = clip(this, from, to);\n let parts = [];\n this.decompose(0, from, parts, 2 /* Open.To */);\n if (text.length)\n text.decompose(0, text.length, parts, 1 /* Open.From */ | 2 /* Open.To */);\n this.decompose(to, this.length, parts, 1 /* Open.From */);\n return TextNode.from(parts, this.length - (to - from) + text.length);\n }\n /**\n Append another document to this one.\n */\n append(other) {\n return this.replace(this.length, this.length, other);\n }\n /**\n Retrieve the text between the given points.\n */\n slice(from, to = this.length) {\n [from, to] = clip(this, from, to);\n let parts = [];\n this.decompose(from, to, parts, 0);\n return TextNode.from(parts, to - from);\n }\n /**\n Test whether this text is equal to another instance.\n */\n eq(other) {\n if (other == this)\n return true;\n if (other.length != this.length || other.lines != this.lines)\n return false;\n let start = this.scanIdentical(other, 1), end = this.length - this.scanIdentical(other, -1);\n let a = new RawTextCursor(this), b = new RawTextCursor(other);\n for (let skip = start, pos = start;;) {\n a.next(skip);\n b.next(skip);\n skip = 0;\n if (a.lineBreak != b.lineBreak || a.done != b.done || a.value != b.value)\n return false;\n pos += a.value.length;\n if (a.done || pos >= end)\n return true;\n }\n }\n /**\n Iterate over the text. When `dir` is `-1`, iteration happens\n from end to start. This will return lines and the breaks between\n them as separate strings.\n */\n iter(dir = 1) { return new RawTextCursor(this, dir); }\n /**\n Iterate over a range of the text. When `from` > `to`, the\n iterator will run in reverse.\n */\n iterRange(from, to = this.length) { return new PartialTextCursor(this, from, to); }\n /**\n Return a cursor that iterates over the given range of lines,\n _without_ returning the line breaks between, and yielding empty\n strings for empty lines.\n \n When `from` and `to` are given, they should be 1-based line numbers.\n */\n iterLines(from, to) {\n let inner;\n if (from == null) {\n inner = this.iter();\n }\n else {\n if (to == null)\n to = this.lines + 1;\n let start = this.line(from).from;\n inner = this.iterRange(start, Math.max(start, to == this.lines + 1 ? this.length : to <= 1 ? 0 : this.line(to - 1).to));\n }\n return new LineCursor(inner);\n }\n /**\n Return the document as a string, using newline characters to\n separate lines.\n */\n toString() { return this.sliceString(0); }\n /**\n Convert the document to an array of lines (which can be\n deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)).\n */\n toJSON() {\n let lines = [];\n this.flatten(lines);\n return lines;\n }\n /**\n @internal\n */\n constructor() { }\n /**\n Create a `Text` instance for the given array of lines.\n */\n static of(text) {\n if (text.length == 0)\n throw new RangeError(\"A document must have at least one line\");\n if (text.length == 1 && !text[0])\n return Text.empty;\n return text.length <= 32 /* Tree.Branch */ ? new TextLeaf(text) : TextNode.from(TextLeaf.split(text, []));\n }\n}\n// Leaves store an array of line strings. There are always line breaks\n// between these strings. Leaves are limited in size and have to be\n// contained in TextNode instances for bigger documents.\nclass TextLeaf extends Text {\n constructor(text, length = textLength(text)) {\n super();\n this.text = text;\n this.length = length;\n }\n get lines() { return this.text.length; }\n get children() { return null; }\n lineInner(target, isLine, line, offset) {\n for (let i = 0;; i++) {\n let string = this.text[i], end = offset + string.length;\n if ((isLine ? line : end) >= target)\n return new Line(offset, end, line, string);\n offset = end + 1;\n line++;\n }\n }\n decompose(from, to, target, open) {\n let text = from <= 0 && to >= this.length ? this\n : new TextLeaf(sliceText(this.text, from, to), Math.min(to, this.length) - Math.max(0, from));\n if (open & 1 /* Open.From */) {\n let prev = target.pop();\n let joined = appendText(text.text, prev.text.slice(), 0, text.length);\n if (joined.length <= 32 /* Tree.Branch */) {\n target.push(new TextLeaf(joined, prev.length + text.length));\n }\n else {\n let mid = joined.length >> 1;\n target.push(new TextLeaf(joined.slice(0, mid)), new TextLeaf(joined.slice(mid)));\n }\n }\n else {\n target.push(text);\n }\n }\n replace(from, to, text) {\n if (!(text instanceof TextLeaf))\n return super.replace(from, to, text);\n [from, to] = clip(this, from, to);\n let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);\n let newLen = this.length + text.length - (to - from);\n if (lines.length <= 32 /* Tree.Branch */)\n return new TextLeaf(lines, newLen);\n return TextNode.from(TextLeaf.split(lines, []), newLen);\n }\n sliceString(from, to = this.length, lineSep = \"\\n\") {\n [from, to] = clip(this, from, to);\n let result = \"\";\n for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {\n let line = this.text[i], end = pos + line.length;\n if (pos > from && i)\n result += lineSep;\n if (from < end && to > pos)\n result += line.slice(Math.max(0, from - pos), to - pos);\n pos = end + 1;\n }\n return result;\n }\n flatten(target) {\n for (let line of this.text)\n target.push(line);\n }\n scanIdentical() { return 0; }\n static split(text, target) {\n let part = [], len = -1;\n for (let line of text) {\n part.push(line);\n len += line.length + 1;\n if (part.length == 32 /* Tree.Branch */) {\n target.push(new TextLeaf(part, len));\n part = [];\n len = -1;\n }\n }\n if (len > -1)\n target.push(new TextLeaf(part, len));\n return target;\n }\n}\n// Nodes provide the tree structure of the `Text` type. They store a\n// number of other nodes or leaves, taking care to balance themselves\n// on changes. There are implied line breaks _between_ the children of\n// a node (but not before the first or after the last child).\nclass TextNode extends Text {\n constructor(children, length) {\n super();\n this.children = children;\n this.length = length;\n this.lines = 0;\n for (let child of children)\n this.lines += child.lines;\n }\n lineInner(target, isLine, line, offset) {\n for (let i = 0;; i++) {\n let child = this.children[i], end = offset + child.length, endLine = line + child.lines - 1;\n if ((isLine ? endLine : end) >= target)\n return child.lineInner(target, isLine, line, offset);\n offset = end + 1;\n line = endLine + 1;\n }\n }\n decompose(from, to, target, open) {\n for (let i = 0, pos = 0; pos <= to && i < this.children.length; i++) {\n let child = this.children[i], end = pos + child.length;\n if (from <= end && to >= pos) {\n let childOpen = open & ((pos <= from ? 1 /* Open.From */ : 0) | (end >= to ? 2 /* Open.To */ : 0));\n if (pos >= from && end <= to && !childOpen)\n target.push(child);\n else\n child.decompose(from - pos, to - pos, target, childOpen);\n }\n pos = end + 1;\n }\n }\n replace(from, to, text) {\n [from, to] = clip(this, from, to);\n if (text.lines < this.lines)\n for (let i = 0, pos = 0; i < this.children.length; i++) {\n let child = this.children[i], end = pos + child.length;\n // Fast path: if the change only affects one child and the\n // child's size remains in the acceptable range, only update\n // that child\n if (from >= pos && to <= end) {\n let updated = child.replace(from - pos, to - pos, text);\n let totalLines = this.lines - child.lines + updated.lines;\n if (updated.lines < (totalLines >> (5 /* Tree.BranchShift */ - 1)) &&\n updated.lines > (totalLines >> (5 /* Tree.BranchShift */ + 1))) {\n let copy = this.children.slice();\n copy[i] = updated;\n return new TextNode(copy, this.length - (to - from) + text.length);\n }\n return super.replace(pos, end, updated);\n }\n pos = end + 1;\n }\n return super.replace(from, to, text);\n }\n sliceString(from, to = this.length, lineSep = \"\\n\") {\n [from, to] = clip(this, from, to);\n let result = \"\";\n for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {\n let child = this.children[i], end = pos + child.length;\n if (pos > from && i)\n result += lineSep;\n if (from < end && to > pos)\n result += child.sliceString(from - pos, to - pos, lineSep);\n pos = end + 1;\n }\n return result;\n }\n flatten(target) {\n for (let child of this.children)\n child.flatten(target);\n }\n scanIdentical(other, dir) {\n if (!(other instanceof TextNode))\n return 0;\n let length = 0;\n let [iA, iB, eA, eB] = dir > 0 ? [0, 0, this.children.length, other.children.length]\n : [this.children.length - 1, other.children.length - 1, -1, -1];\n for (;; iA += dir, iB += dir) {\n if (iA == eA || iB == eB)\n return length;\n let chA = this.children[iA], chB = other.children[iB];\n if (chA != chB)\n return length + chA.scanIdentical(chB, dir);\n length += chA.length + 1;\n }\n }\n static from(children, length = children.reduce((l, ch) => l + ch.length + 1, -1)) {\n let lines = 0;\n for (let ch of children)\n lines += ch.lines;\n if (lines < 32 /* Tree.Branch */) {\n let flat = [];\n for (let ch of children)\n ch.flatten(flat);\n return new TextLeaf(flat, length);\n }\n let chunk = Math.max(32 /* Tree.Branch */, lines >> 5 /* Tree.BranchShift */), maxChunk = chunk << 1, minChunk = chunk >> 1;\n let chunked = [], currentLines = 0, currentLen = -1, currentChunk = [];\n function add(child) {\n let last;\n if (child.lines > maxChunk && child instanceof TextNode) {\n for (let node of child.children)\n add(node);\n }\n else if (child.lines > minChunk && (currentLines > minChunk || !currentLines)) {\n flush();\n chunked.push(child);\n }\n else if (child instanceof TextLeaf && currentLines &&\n (last = currentChunk[currentChunk.length - 1]) instanceof TextLeaf &&\n child.lines + last.lines <= 32 /* Tree.Branch */) {\n currentLines += child.lines;\n currentLen += child.length + 1;\n currentChunk[currentChunk.length - 1] = new TextLeaf(last.text.concat(child.text), last.length + 1 + child.length);\n }\n else {\n if (currentLines + child.lines > chunk)\n flush();\n currentLines += child.lines;\n currentLen += child.length + 1;\n currentChunk.push(child);\n }\n }\n function flush() {\n if (currentLines == 0)\n return;\n chunked.push(currentChunk.length == 1 ? currentChunk[0] : TextNode.from(currentChunk, currentLen));\n currentLen = -1;\n currentLines = currentChunk.length = 0;\n }\n for (let child of children)\n add(child);\n flush();\n return chunked.length == 1 ? chunked[0] : new TextNode(chunked, length);\n }\n}\nText.empty = /*@__PURE__*/new TextLeaf([\"\"], 0);\nfunction textLength(text) {\n let length = -1;\n for (let line of text)\n length += line.length + 1;\n return length;\n}\nfunction appendText(text, target, from = 0, to = 1e9) {\n for (let pos = 0, i = 0, first = true; i < text.length && pos <= to; i++) {\n let line = text[i], end = pos + line.length;\n if (end >= from) {\n if (end > to)\n line = line.slice(0, to - pos);\n if (pos < from)\n line = line.slice(from - pos);\n if (first) {\n target[target.length - 1] += line;\n first = false;\n }\n else\n target.push(line);\n }\n pos = end + 1;\n }\n return target;\n}\nfunction sliceText(text, from, to) {\n return appendText(text, [\"\"], from, to);\n}\nclass RawTextCursor {\n constructor(text, dir = 1) {\n this.dir = dir;\n this.done = false;\n this.lineBreak = false;\n this.value = \"\";\n this.nodes = [text];\n this.offsets = [dir > 0 ? 1 : (text instanceof TextLeaf ? text.text.length : text.children.length) << 1];\n }\n nextInner(skip, dir) {\n this.done = this.lineBreak = false;\n for (;;) {\n let last = this.nodes.length - 1;\n let top = this.nodes[last], offsetValue = this.offsets[last], offset = offsetValue >> 1;\n let size = top instanceof TextLeaf ? top.text.length : top.children.length;\n if (offset == (dir > 0 ? size : 0)) {\n if (last == 0) {\n this.done = true;\n this.value = \"\";\n return this;\n }\n if (dir > 0)\n this.offsets[last - 1]++;\n this.nodes.pop();\n this.offsets.pop();\n }\n else if ((offsetValue & 1) == (dir > 0 ? 0 : 1)) {\n this.offsets[last] += dir;\n if (skip == 0) {\n this.lineBreak = true;\n this.value = \"\\n\";\n return this;\n }\n skip--;\n }\n else if (top instanceof TextLeaf) {\n // Move to the next string\n let next = top.text[offset + (dir < 0 ? -1 : 0)];\n this.offsets[last] += dir;\n if (next.length > Math.max(0, skip)) {\n this.value = skip == 0 ? next : dir > 0 ? next.slice(skip) : next.slice(0, next.length - skip);\n return this;\n }\n skip -= next.length;\n }\n else {\n let next = top.children[offset + (dir < 0 ? -1 : 0)];\n if (skip > next.length) {\n skip -= next.length;\n this.offsets[last] += dir;\n }\n else {\n if (dir < 0)\n this.offsets[last]--;\n this.nodes.push(next);\n this.offsets.push(dir > 0 ? 1 : (next instanceof TextLeaf ? next.text.length : next.children.length) << 1);\n }\n }\n }\n }\n next(skip = 0) {\n if (skip < 0) {\n this.nextInner(-skip, (-this.dir));\n skip = this.value.length;\n }\n return this.nextInner(skip, this.dir);\n }\n}\nclass PartialTextCursor {\n constructor(text, start, end) {\n this.value = \"\";\n this.done = false;\n this.cursor = new RawTextCursor(text, start > end ? -1 : 1);\n this.pos = start > end ? text.length : 0;\n this.from = Math.min(start, end);\n this.to = Math.max(start, end);\n }\n nextInner(skip, dir) {\n if (dir < 0 ? this.pos <= this.from : this.pos >= this.to) {\n this.value = \"\";\n this.done = true;\n return this;\n }\n skip += Math.max(0, dir < 0 ? this.pos - this.to : this.from - this.pos);\n let limit = dir < 0 ? this.pos - this.from : this.to - this.pos;\n if (skip > limit)\n skip = limit;\n limit -= skip;\n let { value } = this.cursor.next(skip);\n this.pos += (value.length + skip) * dir;\n this.value = value.length <= limit ? value : dir < 0 ? value.slice(value.length - limit) : value.slice(0, limit);\n this.done = !this.value;\n return this;\n }\n next(skip = 0) {\n if (skip < 0)\n skip = Math.max(skip, this.from - this.pos);\n else if (skip > 0)\n skip = Math.min(skip, this.to - this.pos);\n return this.nextInner(skip, this.cursor.dir);\n }\n get lineBreak() { return this.cursor.lineBreak && this.value != \"\"; }\n}\nclass LineCursor {\n constructor(inner) {\n this.inner = inner;\n this.afterBreak = true;\n this.value = \"\";\n this.done = false;\n }\n next(skip = 0) {\n let { done, lineBreak, value } = this.inner.next(skip);\n if (done && this.afterBreak) {\n this.value = \"\";\n this.afterBreak = false;\n }\n else if (done) {\n this.done = true;\n this.value = \"\";\n }\n else if (lineBreak) {\n if (this.afterBreak) {\n this.value = \"\";\n }\n else {\n this.afterBreak = true;\n this.next();\n }\n }\n else {\n this.value = value;\n this.afterBreak = false;\n }\n return this;\n }\n get lineBreak() { return false; }\n}\nif (typeof Symbol != \"undefined\") {\n Text.prototype[Symbol.iterator] = function () { return this.iter(); };\n RawTextCursor.prototype[Symbol.iterator] = PartialTextCursor.prototype[Symbol.iterator] =\n LineCursor.prototype[Symbol.iterator] = function () { return this; };\n}\n/**\nThis type describes a line in the document. It is created\non-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt).\n*/\nclass Line {\n /**\n @internal\n */\n constructor(\n /**\n The position of the start of the line.\n */\n from, \n /**\n The position at the end of the line (_before_ the line break,\n or at the end of document for the last line).\n */\n to, \n /**\n This line's line number (1-based).\n */\n number, \n /**\n The line's content.\n */\n text) {\n this.from = from;\n this.to = to;\n this.number = number;\n this.text = text;\n }\n /**\n The length of the line (not including any line break after it).\n */\n get length() { return this.to - this.from; }\n}\nfunction clip(text, from, to) {\n from = Math.max(0, Math.min(text.length, from));\n return [from, Math.max(from, Math.min(text.length, to))];\n}\n\n// Compressed representation of the Grapheme_Cluster_Break=Extend\n// information from\n// http://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt.\n// Each pair of elements represents a range, as an offet from the\n// previous range and a length. Numbers are in base-36, with the empty\n// string being a shorthand for 1.\nlet extend = /*@__PURE__*/\"lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o\".split(\",\").map(s => s ? parseInt(s, 36) : 1);\n// Convert offsets into absolute values\nfor (let i = 1; i < extend.length; i++)\n extend[i] += extend[i - 1];\nfunction isExtendingChar(code) {\n for (let i = 1; i < extend.length; i += 2)\n if (extend[i] > code)\n return extend[i - 1] <= code;\n return false;\n}\nfunction isRegionalIndicator(code) {\n return code >= 0x1F1E6 && code <= 0x1F1FF;\n}\nconst ZWJ = 0x200d;\n/**\nReturns a next grapheme cluster break _after_ (not equal to)\n`pos`, if `forward` is true, or before otherwise. Returns `pos`\nitself if no further cluster break is available in the string.\nMoves across surrogate pairs, extending characters (when\n`includeExtending` is true), characters joined with zero-width\njoiners, and flag emoji.\n*/\nfunction findClusterBreak(str, pos, forward = true, includeExtending = true) {\n return (forward ? nextClusterBreak : prevClusterBreak)(str, pos, includeExtending);\n}\nfunction nextClusterBreak(str, pos, includeExtending) {\n if (pos == str.length)\n return pos;\n // If pos is in the middle of a surrogate pair, move to its start\n if (pos && surrogateLow(str.charCodeAt(pos)) && surrogateHigh(str.charCodeAt(pos - 1)))\n pos--;\n let prev = codePointAt(str, pos);\n pos += codePointSize(prev);\n while (pos < str.length) {\n let next = codePointAt(str, pos);\n if (prev == ZWJ || next == ZWJ || includeExtending && isExtendingChar(next)) {\n pos += codePointSize(next);\n prev = next;\n }\n else if (isRegionalIndicator(next)) {\n let countBefore = 0, i = pos - 2;\n while (i >= 0 && isRegionalIndicator(codePointAt(str, i))) {\n countBefore++;\n i -= 2;\n }\n if (countBefore % 2 == 0)\n break;\n else\n pos += 2;\n }\n else {\n break;\n }\n }\n return pos;\n}\nfunction prevClusterBreak(str, pos, includeExtending) {\n while (pos > 0) {\n let found = nextClusterBreak(str, pos - 2, includeExtending);\n if (found < pos)\n return found;\n pos--;\n }\n return 0;\n}\nfunction surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; }\nfunction surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; }\n/**\nFind the code point at the given position in a string (like the\n[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)\nstring method).\n*/\nfunction codePointAt(str, pos) {\n let code0 = str.charCodeAt(pos);\n if (!surrogateHigh(code0) || pos + 1 == str.length)\n return code0;\n let code1 = str.charCodeAt(pos + 1);\n if (!surrogateLow(code1))\n return code0;\n return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000;\n}\n/**\nGiven a Unicode codepoint, return the JavaScript string that\nrespresents it (like\n[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)).\n*/\nfunction fromCodePoint(code) {\n if (code <= 0xffff)\n return String.fromCharCode(code);\n code -= 0x10000;\n return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00);\n}\n/**\nThe amount of positions a character takes up a JavaScript string.\n*/\nfunction codePointSize(code) { return code < 0x10000 ? 1 : 2; }\n\nconst DefaultSplit = /\\r\\n?|\\n/;\n/**\nDistinguishes different ways in which positions can be mapped.\n*/\nvar MapMode = /*@__PURE__*/(function (MapMode) {\n /**\n Map a position to a valid new position, even when its context\n was deleted.\n */\n MapMode[MapMode[\"Simple\"] = 0] = \"Simple\";\n /**\n Return null if deletion happens across the position.\n */\n MapMode[MapMode[\"TrackDel\"] = 1] = \"TrackDel\";\n /**\n Return null if the character _before_ the position is deleted.\n */\n MapMode[MapMode[\"TrackBefore\"] = 2] = \"TrackBefore\";\n /**\n Return null if the character _after_ the position is deleted.\n */\n MapMode[MapMode[\"TrackAfter\"] = 3] = \"TrackAfter\";\nreturn MapMode})(MapMode || (MapMode = {}));\n/**\nA change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet)\nthat doesn't store the inserted text. As such, it can't be\napplied, but is cheaper to store and manipulate.\n*/\nclass ChangeDesc {\n // Sections are encoded as pairs of integers. The first is the\n // length in the current document, and the second is -1 for\n // unaffected sections, and the length of the replacement content\n // otherwise. So an insertion would be (0, n>0), a deletion (n>0,\n // 0), and a replacement two positive numbers.\n /**\n @internal\n */\n constructor(\n /**\n @internal\n */\n sections) {\n this.sections = sections;\n }\n /**\n The length of the document before the change.\n */\n get length() {\n let result = 0;\n for (let i = 0; i < this.sections.length; i += 2)\n result += this.sections[i];\n return result;\n }\n /**\n The length of the document after the change.\n */\n get newLength() {\n let result = 0;\n for (let i = 0; i < this.sections.length; i += 2) {\n let ins = this.sections[i + 1];\n result += ins < 0 ? this.sections[i] : ins;\n }\n return result;\n }\n /**\n False when there are actual changes in this set.\n */\n get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; }\n /**\n Iterate over the unchanged parts left by these changes. `posA`\n provides the position of the range in the old document, `posB`\n the new position in the changed document.\n */\n iterGaps(f) {\n for (let i = 0, posA = 0, posB = 0; i < this.sections.length;) {\n let len = this.sections[i++], ins = this.sections[i++];\n if (ins < 0) {\n f(posA, posB, len);\n posB += len;\n }\n else {\n posB += ins;\n }\n posA += len;\n }\n }\n /**\n Iterate over the ranges changed by these changes. (See\n [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a\n variant that also provides you with the inserted text.)\n `fromA`/`toA` provides the extent of the change in the starting\n document, `fromB`/`toB` the extent of the replacement in the\n changed document.\n \n When `individual` is true, adjacent changes (which are kept\n separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are\n reported separately.\n */\n iterChangedRanges(f, individual = false) {\n iterChanges(this, f, individual);\n }\n /**\n Get a description of the inverted form of these changes.\n */\n get invertedDesc() {\n let sections = [];\n for (let i = 0; i < this.sections.length;) {\n let len = this.sections[i++], ins = this.sections[i++];\n if (ins < 0)\n sections.push(len, ins);\n else\n sections.push(ins, len);\n }\n return new ChangeDesc(sections);\n }\n /**\n Compute the combined effect of applying another set of changes\n after this one. The length of the document after this set should\n match the length before `other`.\n */\n composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); }\n /**\n Map this description, which should start with the same document\n as `other`, over another set of changes, so that it can be\n applied after it. When `before` is true, map as if the changes\n in `other` happened before the ones in `this`.\n */\n mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); }\n mapPos(pos, assoc = -1, mode = MapMode.Simple) {\n let posA = 0, posB = 0;\n for (let i = 0; i < this.sections.length;) {\n let len = this.sections[i++], ins = this.sections[i++], endA = posA + len;\n if (ins < 0) {\n if (endA > pos)\n return posB + (pos - posA);\n posB += len;\n }\n else {\n if (mode != MapMode.Simple && endA >= pos &&\n (mode == MapMode.TrackDel && posA < pos && endA > pos ||\n mode == MapMode.TrackBefore && posA < pos ||\n mode == MapMode.TrackAfter && endA > pos))\n return null;\n if (endA > pos || endA == pos && assoc < 0 && !len)\n return pos == posA || assoc < 0 ? posB : posB + ins;\n posB += ins;\n }\n posA = endA;\n }\n if (pos > posA)\n throw new RangeError(`Position ${pos} is out of range for changeset of length ${posA}`);\n return posB;\n }\n /**\n Check whether these changes touch a given range. When one of the\n changes entirely covers the range, the string `\"cover\"` is\n returned.\n */\n touchesRange(from, to = from) {\n for (let i = 0, pos = 0; i < this.sections.length && pos <= to;) {\n let len = this.sections[i++], ins = this.sections[i++], end = pos + len;\n if (ins >= 0 && pos <= to && end >= from)\n return pos < from && end > to ? \"cover\" : true;\n pos = end;\n }\n return false;\n }\n /**\n @internal\n */\n toString() {\n let result = \"\";\n for (let i = 0; i < this.sections.length;) {\n let len = this.sections[i++], ins = this.sections[i++];\n result += (result ? \" \" : \"\") + len + (ins >= 0 ? \":\" + ins : \"\");\n }\n return result;\n }\n /**\n Serialize this change desc to a JSON-representable value.\n */\n toJSON() { return this.sections; }\n /**\n Create a change desc from its JSON representation (as produced\n by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON).\n */\n static fromJSON(json) {\n if (!Array.isArray(json) || json.length % 2 || json.some(a => typeof a != \"number\"))\n throw new RangeError(\"Invalid JSON representation of ChangeDesc\");\n return new ChangeDesc(json);\n }\n /**\n @internal\n */\n static create(sections) { return new ChangeDesc(sections); }\n}\n/**\nA change set represents a group of modifications to a document. It\nstores the document length, and can only be applied to documents\nwith exactly that length.\n*/\nclass ChangeSet extends ChangeDesc {\n constructor(sections, \n /**\n @internal\n */\n inserted) {\n super(sections);\n this.inserted = inserted;\n }\n /**\n Apply the changes to a document, returning the modified\n document.\n */\n apply(doc) {\n if (this.length != doc.length)\n throw new RangeError(\"Applying change set to a document with the wrong length\");\n iterChanges(this, (fromA, toA, fromB, _toB, text) => doc = doc.replace(fromB, fromB + (toA - fromA), text), false);\n return doc;\n }\n mapDesc(other, before = false) { return mapSet(this, other, before, true); }\n /**\n Given the document as it existed _before_ the changes, return a\n change set that represents the inverse of this set, which could\n be used to go from the document created by the changes back to\n the document as it existed before the changes.\n */\n invert(doc) {\n let sections = this.sections.slice(), inserted = [];\n for (let i = 0, pos = 0; i < sections.length; i += 2) {\n let len = sections[i], ins = sections[i + 1];\n if (ins >= 0) {\n sections[i] = ins;\n sections[i + 1] = len;\n let index = i >> 1;\n while (inserted.length < index)\n inserted.push(Text.empty);\n inserted.push(len ? doc.slice(pos, pos + len) : Text.empty);\n }\n pos += len;\n }\n return new ChangeSet(sections, inserted);\n }\n /**\n Combine two subsequent change sets into a single set. `other`\n must start in the document produced by `this`. If `this` goes\n `docA` \u2192 `docB` and `other` represents `docB` \u2192 `docC`, the\n returned value will represent the change `docA` \u2192 `docC`.\n */\n compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); }\n /**\n Given another change set starting in the same document, maps this\n change set over the other, producing a new change set that can be\n applied to the document produced by applying `other`. When\n `before` is `true`, order changes as if `this` comes before\n `other`, otherwise (the default) treat `other` as coming first.\n \n Given two changes `A` and `B`, `A.compose(B.map(A))` and\n `B.compose(A.map(B, true))` will produce the same document. This\n provides a basic form of [operational\n transformation](https://en.wikipedia.org/wiki/Operational_transformation),\n and can be used for collaborative editing.\n */\n map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); }\n /**\n Iterate over the changed ranges in the document, calling `f` for\n each, with the range in the original document (`fromA`-`toA`)\n and the range that replaces it in the new document\n (`fromB`-`toB`).\n \n When `individual` is true, adjacent changes are reported\n separately.\n */\n iterChanges(f, individual = false) {\n iterChanges(this, f, individual);\n }\n /**\n Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change\n set.\n */\n get desc() { return ChangeDesc.create(this.sections); }\n /**\n @internal\n */\n filter(ranges) {\n let resultSections = [], resultInserted = [], filteredSections = [];\n let iter = new SectionIter(this);\n done: for (let i = 0, pos = 0;;) {\n let next = i == ranges.length ? 1e9 : ranges[i++];\n while (pos < next || pos == next && iter.len == 0) {\n if (iter.done)\n break done;\n let len = Math.min(iter.len, next - pos);\n addSection(filteredSections, len, -1);\n let ins = iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0;\n addSection(resultSections, len, ins);\n if (ins > 0)\n addInsert(resultInserted, resultSections, iter.text);\n iter.forward(len);\n pos += len;\n }\n let end = ranges[i++];\n while (pos < end) {\n if (iter.done)\n break done;\n let len = Math.min(iter.len, end - pos);\n addSection(resultSections, len, -1);\n addSection(filteredSections, len, iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0);\n iter.forward(len);\n pos += len;\n }\n }\n return { changes: new ChangeSet(resultSections, resultInserted),\n filtered: ChangeDesc.create(filteredSections) };\n }\n /**\n Serialize this change set to a JSON-representable value.\n */\n toJSON() {\n let parts = [];\n for (let i = 0; i < this.sections.length; i += 2) {\n let len = this.sections[i], ins = this.sections[i + 1];\n if (ins < 0)\n parts.push(len);\n else if (ins == 0)\n parts.push([len]);\n else\n parts.push([len].concat(this.inserted[i >> 1].toJSON()));\n }\n return parts;\n }\n /**\n Create a change set for the given changes, for a document of the\n given length, using `lineSep` as line separator.\n */\n static of(changes, length, lineSep) {\n let sections = [], inserted = [], pos = 0;\n let total = null;\n function flush(force = false) {\n if (!force && !sections.length)\n return;\n if (pos < length)\n addSection(sections, length - pos, -1);\n let set = new ChangeSet(sections, inserted);\n total = total ? total.compose(set.map(total)) : set;\n sections = [];\n inserted = [];\n pos = 0;\n }\n function process(spec) {\n if (Array.isArray(spec)) {\n for (let sub of spec)\n process(sub);\n }\n else if (spec instanceof ChangeSet) {\n if (spec.length != length)\n throw new RangeError(`Mismatched change set length (got ${spec.length}, expected ${length})`);\n flush();\n total = total ? total.compose(spec.map(total)) : spec;\n }\n else {\n let { from, to = from, insert } = spec;\n if (from > to || from < 0 || to > length)\n throw new RangeError(`Invalid change range ${from} to ${to} (in doc of length ${length})`);\n let insText = !insert ? Text.empty : typeof insert == \"string\" ? Text.of(insert.split(lineSep || DefaultSplit)) : insert;\n let insLen = insText.length;\n if (from == to && insLen == 0)\n return;\n if (from < pos)\n flush();\n if (from > pos)\n addSection(sections, from - pos, -1);\n addSection(sections, to - from, insLen);\n addInsert(inserted, sections, insText);\n pos = to;\n }\n }\n process(changes);\n flush(!total);\n return total;\n }\n /**\n Create an empty changeset of the given length.\n */\n static empty(length) {\n return new ChangeSet(length ? [length, -1] : [], []);\n }\n /**\n Create a changeset from its JSON representation (as produced by\n [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON).\n */\n static fromJSON(json) {\n if (!Array.isArray(json))\n throw new RangeError(\"Invalid JSON representation of ChangeSet\");\n let sections = [], inserted = [];\n for (let i = 0; i < json.length; i++) {\n let part = json[i];\n if (typeof part == \"number\") {\n sections.push(part, -1);\n }\n else if (!Array.isArray(part) || typeof part[0] != \"number\" || part.some((e, i) => i && typeof e != \"string\")) {\n throw new RangeError(\"Invalid JSON representation of ChangeSet\");\n }\n else if (part.length == 1) {\n sections.push(part[0], 0);\n }\n else {\n while (inserted.length < i)\n inserted.push(Text.empty);\n inserted[i] = Text.of(part.slice(1));\n sections.push(part[0], inserted[i].length);\n }\n }\n return new ChangeSet(sections, inserted);\n }\n /**\n @internal\n */\n static createSet(sections, inserted) {\n return new ChangeSet(sections, inserted);\n }\n}\nfunction addSection(sections, len, ins, forceJoin = false) {\n if (len == 0 && ins <= 0)\n return;\n let last = sections.length - 2;\n if (last >= 0 && ins <= 0 && ins == sections[last + 1])\n sections[last] += len;\n else if (len == 0 && sections[last] == 0)\n sections[last + 1] += ins;\n else if (forceJoin) {\n sections[last] += len;\n sections[last + 1] += ins;\n }\n else\n sections.push(len, ins);\n}\nfunction addInsert(values, sections, value) {\n if (value.length == 0)\n return;\n let index = (sections.length - 2) >> 1;\n if (index < values.length) {\n values[values.length - 1] = values[values.length - 1].append(value);\n }\n else {\n while (values.length < index)\n values.push(Text.empty);\n values.push(value);\n }\n}\nfunction iterChanges(desc, f, individual) {\n let inserted = desc.inserted;\n for (let posA = 0, posB = 0, i = 0; i < desc.sections.length;) {\n let len = desc.sections[i++], ins = desc.sections[i++];\n if (ins < 0) {\n posA += len;\n posB += len;\n }\n else {\n let endA = posA, endB = posB, text = Text.empty;\n for (;;) {\n endA += len;\n endB += ins;\n if (ins && inserted)\n text = text.append(inserted[(i - 2) >> 1]);\n if (individual || i == desc.sections.length || desc.sections[i + 1] < 0)\n break;\n len = desc.sections[i++];\n ins = desc.sections[i++];\n }\n f(posA, endA, posB, endB, text);\n posA = endA;\n posB = endB;\n }\n }\n}\nfunction mapSet(setA, setB, before, mkSet = false) {\n // Produce a copy of setA that applies to the document after setB\n // has been applied (assuming both start at the same document).\n let sections = [], insert = mkSet ? [] : null;\n let a = new SectionIter(setA), b = new SectionIter(setB);\n // Iterate over both sets in parallel. inserted tracks, for changes\n // in A that have to be processed piece-by-piece, whether their\n // content has been inserted already, and refers to the section\n // index.\n for (let inserted = -1;;) {\n if (a.ins == -1 && b.ins == -1) {\n // Move across ranges skipped by both sets.\n let len = Math.min(a.len, b.len);\n addSection(sections, len, -1);\n a.forward(len);\n b.forward(len);\n }\n else if (b.ins >= 0 && (a.ins < 0 || inserted == a.i || a.off == 0 && (b.len < a.len || b.len == a.len && !before))) {\n // If there's a change in B that comes before the next change in\n // A (ordered by start pos, then len, then before flag), skip\n // that (and process any changes in A it covers).\n let len = b.len;\n addSection(sections, b.ins, -1);\n while (len) {\n let piece = Math.min(a.len, len);\n if (a.ins >= 0 && inserted < a.i && a.len <= piece) {\n addSection(sections, 0, a.ins);\n if (insert)\n addInsert(insert, sections, a.text);\n inserted = a.i;\n }\n a.forward(piece);\n len -= piece;\n }\n b.next();\n }\n else if (a.ins >= 0) {\n // Process the part of a change in A up to the start of the next\n // non-deletion change in B (if overlapping).\n let len = 0, left = a.len;\n while (left) {\n if (b.ins == -1) {\n let piece = Math.min(left, b.len);\n len += piece;\n left -= piece;\n b.forward(piece);\n }\n else if (b.ins == 0 && b.len < left) {\n left -= b.len;\n b.next();\n }\n else {\n break;\n }\n }\n addSection(sections, len, inserted < a.i ? a.ins : 0);\n if (insert && inserted < a.i)\n addInsert(insert, sections, a.text);\n inserted = a.i;\n a.forward(a.len - left);\n }\n else if (a.done && b.done) {\n return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections);\n }\n else {\n throw new Error(\"Mismatched change set lengths\");\n }\n }\n}\nfunction composeSets(setA, setB, mkSet = false) {\n let sections = [];\n let insert = mkSet ? [] : null;\n let a = new SectionIter(setA), b = new SectionIter(setB);\n for (let open = false;;) {\n if (a.done && b.done) {\n return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections);\n }\n else if (a.ins == 0) { // Deletion in A\n addSection(sections, a.len, 0, open);\n a.next();\n }\n else if (b.len == 0 && !b.done) { // Insertion in B\n addSection(sections, 0, b.ins, open);\n if (insert)\n addInsert(insert, sections, b.text);\n b.next();\n }\n else if (a.done || b.done) {\n throw new Error(\"Mismatched change set lengths\");\n }\n else {\n let len = Math.min(a.len2, b.len), sectionLen = sections.length;\n if (a.ins == -1) {\n let insB = b.ins == -1 ? -1 : b.off ? 0 : b.ins;\n addSection(sections, len, insB, open);\n if (insert && insB)\n addInsert(insert, sections, b.text);\n }\n else if (b.ins == -1) {\n addSection(sections, a.off ? 0 : a.len, len, open);\n if (insert)\n addInsert(insert, sections, a.textBit(len));\n }\n else {\n addSection(sections, a.off ? 0 : a.len, b.off ? 0 : b.ins, open);\n if (insert && !b.off)\n addInsert(insert, sections, b.text);\n }\n open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen);\n a.forward2(len);\n b.forward(len);\n }\n }\n}\nclass SectionIter {\n constructor(set) {\n this.set = set;\n this.i = 0;\n this.next();\n }\n next() {\n let { sections } = this.set;\n if (this.i < sections.length) {\n this.len = sections[this.i++];\n this.ins = sections[this.i++];\n }\n else {\n this.len = 0;\n this.ins = -2;\n }\n this.off = 0;\n }\n get done() { return this.ins == -2; }\n get len2() { return this.ins < 0 ? this.len : this.ins; }\n get text() {\n let { inserted } = this.set, index = (this.i - 2) >> 1;\n return index >= inserted.length ? Text.empty : inserted[index];\n }\n textBit(len) {\n let { inserted } = this.set, index = (this.i - 2) >> 1;\n return index >= inserted.length && !len ? Text.empty\n : inserted[index].slice(this.off, len == null ? undefined : this.off + len);\n }\n forward(len) {\n if (len == this.len)\n this.next();\n else {\n this.len -= len;\n this.off += len;\n }\n }\n forward2(len) {\n if (this.ins == -1)\n this.forward(len);\n else if (len == this.ins)\n this.next();\n else {\n this.ins -= len;\n this.off += len;\n }\n }\n}\n\n/**\nA single selection range. When\n[`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections)\nis enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold\nmultiple ranges. By default, selections hold exactly one range.\n*/\nclass SelectionRange {\n constructor(\n /**\n The lower boundary of the range.\n */\n from, \n /**\n The upper boundary of the range.\n */\n to, flags) {\n this.from = from;\n this.to = to;\n this.flags = flags;\n }\n /**\n The anchor of the range\u2014the side that doesn't move when you\n extend it.\n */\n get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; }\n /**\n The head of the range, which is moved when the range is\n [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend).\n */\n get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; }\n /**\n True when `anchor` and `head` are at the same position.\n */\n get empty() { return this.from == this.to; }\n /**\n If this is a cursor that is explicitly associated with the\n character on one of its sides, this returns the side. -1 means\n the character before its position, 1 the character after, and 0\n means no association.\n */\n get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; }\n /**\n The bidirectional text level associated with this cursor, if\n any.\n */\n get bidiLevel() {\n let level = this.flags & 7 /* RangeFlag.BidiLevelMask */;\n return level == 7 ? null : level;\n }\n /**\n The goal column (stored vertical offset) associated with a\n cursor. This is used to preserve the vertical position when\n [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across\n lines of different length.\n */\n get goalColumn() {\n let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */;\n return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value;\n }\n /**\n Map this range through a change, producing a valid range in the\n updated document.\n */\n map(change, assoc = -1) {\n let from, to;\n if (this.empty) {\n from = to = change.mapPos(this.from, assoc);\n }\n else {\n from = change.mapPos(this.from, 1);\n to = change.mapPos(this.to, -1);\n }\n return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags);\n }\n /**\n Extend this range to cover at least `from` to `to`.\n */\n extend(from, to = from) {\n if (from <= this.anchor && to >= this.anchor)\n return EditorSelection.range(from, to);\n let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to;\n return EditorSelection.range(this.anchor, head);\n }\n /**\n Compare this range to another range.\n */\n eq(other, includeAssoc = false) {\n return this.anchor == other.anchor && this.head == other.head &&\n (!includeAssoc || !this.empty || this.assoc == other.assoc);\n }\n /**\n Return a JSON-serializable object representing the range.\n */\n toJSON() { return { anchor: this.anchor, head: this.head }; }\n /**\n Convert a JSON representation of a range to a `SelectionRange`\n instance.\n */\n static fromJSON(json) {\n if (!json || typeof json.anchor != \"number\" || typeof json.head != \"number\")\n throw new RangeError(\"Invalid JSON representation for SelectionRange\");\n return EditorSelection.range(json.anchor, json.head);\n }\n /**\n @internal\n */\n static create(from, to, flags) {\n return new SelectionRange(from, to, flags);\n }\n}\n/**\nAn editor selection holds one or more selection ranges.\n*/\nclass EditorSelection {\n constructor(\n /**\n The ranges in the selection, sorted by position. Ranges cannot\n overlap (but they may touch, if they aren't empty).\n */\n ranges, \n /**\n The index of the _main_ range in the selection (which is\n usually the range that was added last).\n */\n mainIndex) {\n this.ranges = ranges;\n this.mainIndex = mainIndex;\n }\n /**\n Map a selection through a change. Used to adjust the selection\n position for changes.\n */\n map(change, assoc = -1) {\n if (change.empty)\n return this;\n return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);\n }\n /**\n Compare this selection to another selection. By default, ranges\n are compared only by position. When `includeAssoc` is true,\n cursor ranges must also have the same\n [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.\n */\n eq(other, includeAssoc = false) {\n if (this.ranges.length != other.ranges.length ||\n this.mainIndex != other.mainIndex)\n return false;\n for (let i = 0; i < this.ranges.length; i++)\n if (!this.ranges[i].eq(other.ranges[i], includeAssoc))\n return false;\n return true;\n }\n /**\n Get the primary selection range. Usually, you should make sure\n your code applies to _all_ ranges, by using methods like\n [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange).\n */\n get main() { return this.ranges[this.mainIndex]; }\n /**\n Make sure the selection only has one range. Returns a selection\n holding only the main range from this selection.\n */\n asSingle() {\n return this.ranges.length == 1 ? this : new EditorSelection([this.main], 0);\n }\n /**\n Extend this selection with an extra range.\n */\n addRange(range, main = true) {\n return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1);\n }\n /**\n Replace a given range with another range, and then normalize the\n selection to merge and sort ranges if necessary.\n */\n replaceRange(range, which = this.mainIndex) {\n let ranges = this.ranges.slice();\n ranges[which] = range;\n return EditorSelection.create(ranges, this.mainIndex);\n }\n /**\n Convert this selection to an object that can be serialized to\n JSON.\n */\n toJSON() {\n return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex };\n }\n /**\n Create a selection from a JSON representation.\n */\n static fromJSON(json) {\n if (!json || !Array.isArray(json.ranges) || typeof json.main != \"number\" || json.main >= json.ranges.length)\n throw new RangeError(\"Invalid JSON representation for EditorSelection\");\n return new EditorSelection(json.ranges.map((r) => SelectionRange.fromJSON(r)), json.main);\n }\n /**\n Create a selection holding a single range.\n */\n static single(anchor, head = anchor) {\n return new EditorSelection([EditorSelection.range(anchor, head)], 0);\n }\n /**\n Sort and merge the given set of ranges, creating a valid\n selection.\n */\n static create(ranges, mainIndex = 0) {\n if (ranges.length == 0)\n throw new RangeError(\"A selection needs at least one range\");\n for (let pos = 0, i = 0; i < ranges.length; i++) {\n let range = ranges[i];\n if (range.empty ? range.from <= pos : range.from < pos)\n return EditorSelection.normalized(ranges.slice(), mainIndex);\n pos = range.to;\n }\n return new EditorSelection(ranges, mainIndex);\n }\n /**\n Create a cursor selection range at the given position. You can\n safely ignore the optional arguments in most situations.\n */\n static cursor(pos, assoc = 0, bidiLevel, goalColumn) {\n return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) |\n (bidiLevel == null ? 7 : Math.min(6, bidiLevel)) |\n ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */));\n }\n /**\n Create a selection range.\n */\n static range(anchor, head, goalColumn, bidiLevel) {\n let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) |\n (bidiLevel == null ? 7 : Math.min(6, bidiLevel));\n return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags)\n : SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags);\n }\n /**\n @internal\n */\n static normalized(ranges, mainIndex = 0) {\n let main = ranges[mainIndex];\n ranges.sort((a, b) => a.from - b.from);\n mainIndex = ranges.indexOf(main);\n for (let i = 1; i < ranges.length; i++) {\n let range = ranges[i], prev = ranges[i - 1];\n if (range.empty ? range.from <= prev.to : range.from < prev.to) {\n let from = prev.from, to = Math.max(range.to, prev.to);\n if (i <= mainIndex)\n mainIndex--;\n ranges.splice(--i, 2, range.anchor > range.head ? EditorSelection.range(to, from) : EditorSelection.range(from, to));\n }\n }\n return new EditorSelection(ranges, mainIndex);\n }\n}\nfunction checkSelection(selection, docLength) {\n for (let range of selection.ranges)\n if (range.to > docLength)\n throw new RangeError(\"Selection points outside of document\");\n}\n\nlet nextID = 0;\n/**\nA facet is a labeled value that is associated with an editor\nstate. It takes inputs from any number of extensions, and combines\nthose into a single output value.\n\nExamples of uses of facets are the [tab\nsize](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor\nattributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update\nlisteners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener).\n\nNote that `Facet` instances can be used anywhere where\n[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected.\n*/\nclass Facet {\n constructor(\n /**\n @internal\n */\n combine, \n /**\n @internal\n */\n compareInput, \n /**\n @internal\n */\n compare, isStatic, enables) {\n this.combine = combine;\n this.compareInput = compareInput;\n this.compare = compare;\n this.isStatic = isStatic;\n /**\n @internal\n */\n this.id = nextID++;\n this.default = combine([]);\n this.extensions = typeof enables == \"function\" ? enables(this) : enables;\n }\n /**\n Returns a facet reader for this facet, which can be used to\n [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it.\n */\n get reader() { return this; }\n /**\n Define a new facet.\n */\n static define(config = {}) {\n return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray : (a, b) => a === b), !!config.static, config.enables);\n }\n /**\n Returns an extension that adds the given value to this facet.\n */\n of(value) {\n return new FacetProvider([], this, 0 /* Provider.Static */, value);\n }\n /**\n Create an extension that computes a value for the facet from a\n state. You must take care to declare the parts of the state that\n this value depends on, since your function is only called again\n for a new state when one of those parts changed.\n \n In cases where your value depends only on a single field, you'll\n want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead.\n */\n compute(deps, get) {\n if (this.isStatic)\n throw new Error(\"Can't compute a static facet\");\n return new FacetProvider(deps, this, 1 /* Provider.Single */, get);\n }\n /**\n Create an extension that computes zero or more values for this\n facet from a state.\n */\n computeN(deps, get) {\n if (this.isStatic)\n throw new Error(\"Can't compute a static facet\");\n return new FacetProvider(deps, this, 2 /* Provider.Multi */, get);\n }\n from(field, get) {\n if (!get)\n get = x => x;\n return this.compute([field], state => get(state.field(field)));\n }\n}\nfunction sameArray(a, b) {\n return a == b || a.length == b.length && a.every((e, i) => e === b[i]);\n}\nclass FacetProvider {\n constructor(dependencies, facet, type, value) {\n this.dependencies = dependencies;\n this.facet = facet;\n this.type = type;\n this.value = value;\n this.id = nextID++;\n }\n dynamicSlot(addresses) {\n var _a;\n let getter = this.value;\n let compare = this.facet.compareInput;\n let id = this.id, idx = addresses[id] >> 1, multi = this.type == 2 /* Provider.Multi */;\n let depDoc = false, depSel = false, depAddrs = [];\n for (let dep of this.dependencies) {\n if (dep == \"doc\")\n depDoc = true;\n else if (dep == \"selection\")\n depSel = true;\n else if ((((_a = addresses[dep.id]) !== null && _a !== void 0 ? _a : 1) & 1) == 0)\n depAddrs.push(addresses[dep.id]);\n }\n return {\n create(state) {\n state.values[idx] = getter(state);\n return 1 /* SlotStatus.Changed */;\n },\n update(state, tr) {\n if ((depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || ensureAll(state, depAddrs)) {\n let newVal = getter(state);\n if (multi ? !compareArray(newVal, state.values[idx], compare) : !compare(newVal, state.values[idx])) {\n state.values[idx] = newVal;\n return 1 /* SlotStatus.Changed */;\n }\n }\n return 0;\n },\n reconfigure: (state, oldState) => {\n let newVal, oldAddr = oldState.config.address[id];\n if (oldAddr != null) {\n let oldVal = getAddr(oldState, oldAddr);\n if (this.dependencies.every(dep => {\n return dep instanceof Facet ? oldState.facet(dep) === state.facet(dep) :\n dep instanceof StateField ? oldState.field(dep, false) == state.field(dep, false) : true;\n }) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) {\n state.values[idx] = oldVal;\n return 0;\n }\n }\n else {\n newVal = getter(state);\n }\n state.values[idx] = newVal;\n return 1 /* SlotStatus.Changed */;\n }\n };\n }\n}\nfunction compareArray(a, b, compare) {\n if (a.length != b.length)\n return false;\n for (let i = 0; i < a.length; i++)\n if (!compare(a[i], b[i]))\n return false;\n return true;\n}\nfunction ensureAll(state, addrs) {\n let changed = false;\n for (let addr of addrs)\n if (ensureAddr(state, addr) & 1 /* SlotStatus.Changed */)\n changed = true;\n return changed;\n}\nfunction dynamicFacetSlot(addresses, facet, providers) {\n let providerAddrs = providers.map(p => addresses[p.id]);\n let providerTypes = providers.map(p => p.type);\n let dynamic = providerAddrs.filter(p => !(p & 1));\n let idx = addresses[facet.id] >> 1;\n function get(state) {\n let values = [];\n for (let i = 0; i < providerAddrs.length; i++) {\n let value = getAddr(state, providerAddrs[i]);\n if (providerTypes[i] == 2 /* Provider.Multi */)\n for (let val of value)\n values.push(val);\n else\n values.push(value);\n }\n return facet.combine(values);\n }\n return {\n create(state) {\n for (let addr of providerAddrs)\n ensureAddr(state, addr);\n state.values[idx] = get(state);\n return 1 /* SlotStatus.Changed */;\n },\n update(state, tr) {\n if (!ensureAll(state, dynamic))\n return 0;\n let value = get(state);\n if (facet.compare(value, state.values[idx]))\n return 0;\n state.values[idx] = value;\n return 1 /* SlotStatus.Changed */;\n },\n reconfigure(state, oldState) {\n let depChanged = ensureAll(state, providerAddrs);\n let oldProviders = oldState.config.facets[facet.id], oldValue = oldState.facet(facet);\n if (oldProviders && !depChanged && sameArray(providers, oldProviders)) {\n state.values[idx] = oldValue;\n return 0;\n }\n let value = get(state);\n if (facet.compare(value, oldValue)) {\n state.values[idx] = oldValue;\n return 0;\n }\n state.values[idx] = value;\n return 1 /* SlotStatus.Changed */;\n }\n };\n}\nconst initField = /*@__PURE__*/Facet.define({ static: true });\n/**\nFields can store additional information in an editor state, and\nkeep it in sync with the rest of the state.\n*/\nclass StateField {\n constructor(\n /**\n @internal\n */\n id, createF, updateF, compareF, \n /**\n @internal\n */\n spec) {\n this.id = id;\n this.createF = createF;\n this.updateF = updateF;\n this.compareF = compareF;\n this.spec = spec;\n /**\n @internal\n */\n this.provides = undefined;\n }\n /**\n Define a state field.\n */\n static define(config) {\n let field = new StateField(nextID++, config.create, config.update, config.compare || ((a, b) => a === b), config);\n if (config.provide)\n field.provides = config.provide(field);\n return field;\n }\n create(state) {\n let init = state.facet(initField).find(i => i.field == this);\n return ((init === null || init === void 0 ? void 0 : init.create) || this.createF)(state);\n }\n /**\n @internal\n */\n slot(addresses) {\n let idx = addresses[this.id] >> 1;\n return {\n create: (state) => {\n state.values[idx] = this.create(state);\n return 1 /* SlotStatus.Changed */;\n },\n update: (state, tr) => {\n let oldVal = state.values[idx];\n let value = this.updateF(oldVal, tr);\n if (this.compareF(oldVal, value))\n return 0;\n state.values[idx] = value;\n return 1 /* SlotStatus.Changed */;\n },\n reconfigure: (state, oldState) => {\n if (oldState.config.address[this.id] != null) {\n state.values[idx] = oldState.field(this);\n return 0;\n }\n state.values[idx] = this.create(state);\n return 1 /* SlotStatus.Changed */;\n }\n };\n }\n /**\n Returns an extension that enables this field and overrides the\n way it is initialized. Can be useful when you need to provide a\n non-default starting value for the field.\n */\n init(create) {\n return [this, initField.of({ field: this, create })];\n }\n /**\n State field instances can be used as\n [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a\n given state.\n */\n get extension() { return this; }\n}\nconst Prec_ = { lowest: 4, low: 3, default: 2, high: 1, highest: 0 };\nfunction prec(value) {\n return (ext) => new PrecExtension(ext, value);\n}\n/**\nBy default extensions are registered in the order they are found\nin the flattened form of nested array that was provided.\nIndividual extension values can be assigned a precedence to\noverride this. Extensions that do not have a precedence set get\nthe precedence of the nearest parent with a precedence, or\n[`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The\nfinal ordering of extensions is determined by first sorting by\nprecedence and then by order within each precedence.\n*/\nconst Prec = {\n /**\n The highest precedence level, for extensions that should end up\n near the start of the precedence ordering.\n */\n highest: /*@__PURE__*/prec(Prec_.highest),\n /**\n A higher-than-default precedence, for extensions that should\n come before those with default precedence.\n */\n high: /*@__PURE__*/prec(Prec_.high),\n /**\n The default precedence, which is also used for extensions\n without an explicit precedence.\n */\n default: /*@__PURE__*/prec(Prec_.default),\n /**\n A lower-than-default precedence.\n */\n low: /*@__PURE__*/prec(Prec_.low),\n /**\n The lowest precedence level. Meant for things that should end up\n near the end of the extension order.\n */\n lowest: /*@__PURE__*/prec(Prec_.lowest)\n};\nclass PrecExtension {\n constructor(inner, prec) {\n this.inner = inner;\n this.prec = prec;\n }\n}\n/**\nExtension compartments can be used to make a configuration\ndynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your\nconfiguration in a compartment, you can later\n[replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a\ntransaction.\n*/\nclass Compartment {\n /**\n Create an instance of this compartment to add to your [state\n configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions).\n */\n of(ext) { return new CompartmentInstance(this, ext); }\n /**\n Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that\n reconfigures this compartment.\n */\n reconfigure(content) {\n return Compartment.reconfigure.of({ compartment: this, extension: content });\n }\n /**\n Get the current content of the compartment in the state, or\n `undefined` if it isn't present.\n */\n get(state) {\n return state.config.compartments.get(this);\n }\n}\nclass CompartmentInstance {\n constructor(compartment, inner) {\n this.compartment = compartment;\n this.inner = inner;\n }\n}\nclass Configuration {\n constructor(base, compartments, dynamicSlots, address, staticValues, facets) {\n this.base = base;\n this.compartments = compartments;\n this.dynamicSlots = dynamicSlots;\n this.address = address;\n this.staticValues = staticValues;\n this.facets = facets;\n this.statusTemplate = [];\n while (this.statusTemplate.length < dynamicSlots.length)\n this.statusTemplate.push(0 /* SlotStatus.Unresolved */);\n }\n staticFacet(facet) {\n let addr = this.address[facet.id];\n return addr == null ? facet.default : this.staticValues[addr >> 1];\n }\n static resolve(base, compartments, oldState) {\n let fields = [];\n let facets = Object.create(null);\n let newCompartments = new Map();\n for (let ext of flatten(base, compartments, newCompartments)) {\n if (ext instanceof StateField)\n fields.push(ext);\n else\n (facets[ext.facet.id] || (facets[ext.facet.id] = [])).push(ext);\n }\n let address = Object.create(null);\n let staticValues = [];\n let dynamicSlots = [];\n for (let field of fields) {\n address[field.id] = dynamicSlots.length << 1;\n dynamicSlots.push(a => field.slot(a));\n }\n let oldFacets = oldState === null || oldState === void 0 ? void 0 : oldState.config.facets;\n for (let id in facets) {\n let providers = facets[id], facet = providers[0].facet;\n let oldProviders = oldFacets && oldFacets[id] || [];\n if (providers.every(p => p.type == 0 /* Provider.Static */)) {\n address[facet.id] = (staticValues.length << 1) | 1;\n if (sameArray(oldProviders, providers)) {\n staticValues.push(oldState.facet(facet));\n }\n else {\n let value = facet.combine(providers.map(p => p.value));\n staticValues.push(oldState && facet.compare(value, oldState.facet(facet)) ? oldState.facet(facet) : value);\n }\n }\n else {\n for (let p of providers) {\n if (p.type == 0 /* Provider.Static */) {\n address[p.id] = (staticValues.length << 1) | 1;\n staticValues.push(p.value);\n }\n else {\n address[p.id] = dynamicSlots.length << 1;\n dynamicSlots.push(a => p.dynamicSlot(a));\n }\n }\n address[facet.id] = dynamicSlots.length << 1;\n dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers));\n }\n }\n let dynamic = dynamicSlots.map(f => f(address));\n return new Configuration(base, newCompartments, dynamic, address, staticValues, facets);\n }\n}\nfunction flatten(extension, compartments, newCompartments) {\n let result = [[], [], [], [], []];\n let seen = new Map();\n function inner(ext, prec) {\n let known = seen.get(ext);\n if (known != null) {\n if (known <= prec)\n return;\n let found = result[known].indexOf(ext);\n if (found > -1)\n result[known].splice(found, 1);\n if (ext instanceof CompartmentInstance)\n newCompartments.delete(ext.compartment);\n }\n seen.set(ext, prec);\n if (Array.isArray(ext)) {\n for (let e of ext)\n inner(e, prec);\n }\n else if (ext instanceof CompartmentInstance) {\n if (newCompartments.has(ext.compartment))\n throw new RangeError(`Duplicate use of compartment in extensions`);\n let content = compartments.get(ext.compartment) || ext.inner;\n newCompartments.set(ext.compartment, content);\n inner(content, prec);\n }\n else if (ext instanceof PrecExtension) {\n inner(ext.inner, ext.prec);\n }\n else if (ext instanceof StateField) {\n result[prec].push(ext);\n if (ext.provides)\n inner(ext.provides, prec);\n }\n else if (ext instanceof FacetProvider) {\n result[prec].push(ext);\n if (ext.facet.extensions)\n inner(ext.facet.extensions, Prec_.default);\n }\n else {\n let content = ext.extension;\n if (!content)\n throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);\n inner(content, prec);\n }\n }\n inner(extension, Prec_.default);\n return result.reduce((a, b) => a.concat(b));\n}\nfunction ensureAddr(state, addr) {\n if (addr & 1)\n return 2 /* SlotStatus.Computed */;\n let idx = addr >> 1;\n let status = state.status[idx];\n if (status == 4 /* SlotStatus.Computing */)\n throw new Error(\"Cyclic dependency between fields and/or facets\");\n if (status & 2 /* SlotStatus.Computed */)\n return status;\n state.status[idx] = 4 /* SlotStatus.Computing */;\n let changed = state.computeSlot(state, state.config.dynamicSlots[idx]);\n return state.status[idx] = 2 /* SlotStatus.Computed */ | changed;\n}\nfunction getAddr(state, addr) {\n return addr & 1 ? state.config.staticValues[addr >> 1] : state.values[addr >> 1];\n}\n\nconst languageData = /*@__PURE__*/Facet.define();\nconst allowMultipleSelections = /*@__PURE__*/Facet.define({\n combine: values => values.some(v => v),\n static: true\n});\nconst lineSeparator = /*@__PURE__*/Facet.define({\n combine: values => values.length ? values[0] : undefined,\n static: true\n});\nconst changeFilter = /*@__PURE__*/Facet.define();\nconst transactionFilter = /*@__PURE__*/Facet.define();\nconst transactionExtender = /*@__PURE__*/Facet.define();\nconst readOnly = /*@__PURE__*/Facet.define({\n combine: values => values.length ? values[0] : false\n});\n\n/**\nAnnotations are tagged values that are used to add metadata to\ntransactions in an extensible way. They should be used to model\nthings that effect the entire transaction (such as its [time\nstamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its\n[origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen\n_alongside_ the other changes made by the transaction, [state\neffects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate.\n*/\nclass Annotation {\n /**\n @internal\n */\n constructor(\n /**\n The annotation type.\n */\n type, \n /**\n The value of this annotation.\n */\n value) {\n this.type = type;\n this.value = value;\n }\n /**\n Define a new type of annotation.\n */\n static define() { return new AnnotationType(); }\n}\n/**\nMarker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation).\n*/\nclass AnnotationType {\n /**\n Create an instance of this annotation.\n */\n of(value) { return new Annotation(this, value); }\n}\n/**\nRepresentation of a type of state effect. Defined with\n[`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define).\n*/\nclass StateEffectType {\n /**\n @internal\n */\n constructor(\n // The `any` types in these function types are there to work\n // around TypeScript issue #37631, where the type guard on\n // `StateEffect.is` mysteriously stops working when these properly\n // have type `Value`.\n /**\n @internal\n */\n map) {\n this.map = map;\n }\n /**\n Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this\n type.\n */\n of(value) { return new StateEffect(this, value); }\n}\n/**\nState effects can be used to represent additional effects\nassociated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They\nare often useful to model changes to custom [state\nfields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in\ndocument or selection changes.\n*/\nclass StateEffect {\n /**\n @internal\n */\n constructor(\n /**\n @internal\n */\n type, \n /**\n The value of this effect.\n */\n value) {\n this.type = type;\n this.value = value;\n }\n /**\n Map this effect through a position mapping. Will return\n `undefined` when that ends up deleting the effect.\n */\n map(mapping) {\n let mapped = this.type.map(this.value, mapping);\n return mapped === undefined ? undefined : mapped == this.value ? this : new StateEffect(this.type, mapped);\n }\n /**\n Tells you whether this effect object is of a given\n [type](https://codemirror.net/6/docs/ref/#state.StateEffectType).\n */\n is(type) { return this.type == type; }\n /**\n Define a new effect type. The type parameter indicates the type\n of values that his effect holds. It should be a type that\n doesn't include `undefined`, since that is used in\n [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is\n removed.\n */\n static define(spec = {}) {\n return new StateEffectType(spec.map || (v => v));\n }\n /**\n Map an array of effects through a change set.\n */\n static mapEffects(effects, mapping) {\n if (!effects.length)\n return effects;\n let result = [];\n for (let effect of effects) {\n let mapped = effect.map(mapping);\n if (mapped)\n result.push(mapped);\n }\n return result;\n }\n}\n/**\nThis effect can be used to reconfigure the root extensions of\nthe editor. Doing this will discard any extensions\n[appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset\nthe content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure)\ncompartments.\n*/\nStateEffect.reconfigure = /*@__PURE__*/StateEffect.define();\n/**\nAppend extensions to the top-level configuration of the editor.\n*/\nStateEffect.appendConfig = /*@__PURE__*/StateEffect.define();\n/**\nChanges to the editor state are grouped into transactions.\nTypically, a user action creates a single transaction, which may\ncontain any number of document changes, may change the selection,\nor have other effects. Create a transaction by calling\n[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately\ndispatch one by calling\n[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch).\n*/\nclass Transaction {\n constructor(\n /**\n The state from which the transaction starts.\n */\n startState, \n /**\n The document changes made by this transaction.\n */\n changes, \n /**\n The selection set by this transaction, or undefined if it\n doesn't explicitly set a selection.\n */\n selection, \n /**\n The effects added to the transaction.\n */\n effects, \n /**\n @internal\n */\n annotations, \n /**\n Whether the selection should be scrolled into view after this\n transaction is dispatched.\n */\n scrollIntoView) {\n this.startState = startState;\n this.changes = changes;\n this.selection = selection;\n this.effects = effects;\n this.annotations = annotations;\n this.scrollIntoView = scrollIntoView;\n /**\n @internal\n */\n this._doc = null;\n /**\n @internal\n */\n this._state = null;\n if (selection)\n checkSelection(selection, changes.newLength);\n if (!annotations.some((a) => a.type == Transaction.time))\n this.annotations = annotations.concat(Transaction.time.of(Date.now()));\n }\n /**\n @internal\n */\n static create(startState, changes, selection, effects, annotations, scrollIntoView) {\n return new Transaction(startState, changes, selection, effects, annotations, scrollIntoView);\n }\n /**\n The new document produced by the transaction. Contrary to\n [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't\n force the entire new state to be computed right away, so it is\n recommended that [transaction\n filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter\n when they need to look at the new document.\n */\n get newDoc() {\n return this._doc || (this._doc = this.changes.apply(this.startState.doc));\n }\n /**\n The new selection produced by the transaction. If\n [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined,\n this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's\n current selection through the changes made by the transaction.\n */\n get newSelection() {\n return this.selection || this.startState.selection.map(this.changes);\n }\n /**\n The new state created by the transaction. Computed on demand\n (but retained for subsequent access), so it is recommended not to\n access it in [transaction\n filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible.\n */\n get state() {\n if (!this._state)\n this.startState.applyTransaction(this);\n return this._state;\n }\n /**\n Get the value of the given annotation type, if any.\n */\n annotation(type) {\n for (let ann of this.annotations)\n if (ann.type == type)\n return ann.value;\n return undefined;\n }\n /**\n Indicates whether the transaction changed the document.\n */\n get docChanged() { return !this.changes.empty; }\n /**\n Indicates whether this transaction reconfigures the state\n (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or\n with a top-level configuration\n [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure).\n */\n get reconfigured() { return this.startState.config != this.state.config; }\n /**\n Returns true if the transaction has a [user\n event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to\n or more specific than `event`. For example, if the transaction\n has `\"select.pointer\"` as user event, `\"select\"` and\n `\"select.pointer\"` will match it.\n */\n isUserEvent(event) {\n let e = this.annotation(Transaction.userEvent);\n return !!(e && (e == event || e.length > event.length && e.slice(0, event.length) == event && e[event.length] == \".\"));\n }\n}\n/**\nAnnotation used to store transaction timestamps. Automatically\nadded to every transaction, holding `Date.now()`.\n*/\nTransaction.time = /*@__PURE__*/Annotation.define();\n/**\nAnnotation used to associate a transaction with a user interface\nevent. Holds a string identifying the event, using a\ndot-separated format to support attaching more specific\ninformation. The events used by the core libraries are:\n\n - `\"input\"` when content is entered\n - `\"input.type\"` for typed input\n - `\"input.type.compose\"` for composition\n - `\"input.paste\"` for pasted input\n - `\"input.drop\"` when adding content with drag-and-drop\n - `\"input.complete\"` when autocompleting\n - `\"delete\"` when the user deletes content\n - `\"delete.selection\"` when deleting the selection\n - `\"delete.forward\"` when deleting forward from the selection\n - `\"delete.backward\"` when deleting backward from the selection\n - `\"delete.cut\"` when cutting to the clipboard\n - `\"move\"` when content is moved\n - `\"move.drop\"` when content is moved within the editor through drag-and-drop\n - `\"select\"` when explicitly changing the selection\n - `\"select.pointer\"` when selecting with a mouse or other pointing device\n - `\"undo\"` and `\"redo\"` for history actions\n\nUse [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check\nwhether the annotation matches a given event.\n*/\nTransaction.userEvent = /*@__PURE__*/Annotation.define();\n/**\nAnnotation indicating whether a transaction should be added to\nthe undo history or not.\n*/\nTransaction.addToHistory = /*@__PURE__*/Annotation.define();\n/**\nAnnotation indicating (when present and true) that a transaction\nrepresents a change made by some other actor, not the user. This\nis used, for example, to tag other people's changes in\ncollaborative editing.\n*/\nTransaction.remote = /*@__PURE__*/Annotation.define();\nfunction joinRanges(a, b) {\n let result = [];\n for (let iA = 0, iB = 0;;) {\n let from, to;\n if (iA < a.length && (iB == b.length || b[iB] >= a[iA])) {\n from = a[iA++];\n to = a[iA++];\n }\n else if (iB < b.length) {\n from = b[iB++];\n to = b[iB++];\n }\n else\n return result;\n if (!result.length || result[result.length - 1] < from)\n result.push(from, to);\n else if (result[result.length - 1] < to)\n result[result.length - 1] = to;\n }\n}\nfunction mergeTransaction(a, b, sequential) {\n var _a;\n let mapForA, mapForB, changes;\n if (sequential) {\n mapForA = b.changes;\n mapForB = ChangeSet.empty(b.changes.length);\n changes = a.changes.compose(b.changes);\n }\n else {\n mapForA = b.changes.map(a.changes);\n mapForB = a.changes.mapDesc(b.changes, true);\n changes = a.changes.compose(mapForA);\n }\n return {\n changes,\n selection: b.selection ? b.selection.map(mapForB) : (_a = a.selection) === null || _a === void 0 ? void 0 : _a.map(mapForA),\n effects: StateEffect.mapEffects(a.effects, mapForA).concat(StateEffect.mapEffects(b.effects, mapForB)),\n annotations: a.annotations.length ? a.annotations.concat(b.annotations) : b.annotations,\n scrollIntoView: a.scrollIntoView || b.scrollIntoView\n };\n}\nfunction resolveTransactionInner(state, spec, docSize) {\n let sel = spec.selection, annotations = asArray(spec.annotations);\n if (spec.userEvent)\n annotations = annotations.concat(Transaction.userEvent.of(spec.userEvent));\n return {\n changes: spec.changes instanceof ChangeSet ? spec.changes\n : ChangeSet.of(spec.changes || [], docSize, state.facet(lineSeparator)),\n selection: sel && (sel instanceof EditorSelection ? sel : EditorSelection.single(sel.anchor, sel.head)),\n effects: asArray(spec.effects),\n annotations,\n scrollIntoView: !!spec.scrollIntoView\n };\n}\nfunction resolveTransaction(state, specs, filter) {\n let s = resolveTransactionInner(state, specs.length ? specs[0] : {}, state.doc.length);\n if (specs.length && specs[0].filter === false)\n filter = false;\n for (let i = 1; i < specs.length; i++) {\n if (specs[i].filter === false)\n filter = false;\n let seq = !!specs[i].sequential;\n s = mergeTransaction(s, resolveTransactionInner(state, specs[i], seq ? s.changes.newLength : state.doc.length), seq);\n }\n let tr = Transaction.create(state, s.changes, s.selection, s.effects, s.annotations, s.scrollIntoView);\n return extendTransaction(filter ? filterTransaction(tr) : tr);\n}\n// Finish a transaction by applying filters if necessary.\nfunction filterTransaction(tr) {\n let state = tr.startState;\n // Change filters\n let result = true;\n for (let filter of state.facet(changeFilter)) {\n let value = filter(tr);\n if (value === false) {\n result = false;\n break;\n }\n if (Array.isArray(value))\n result = result === true ? value : joinRanges(result, value);\n }\n if (result !== true) {\n let changes, back;\n if (result === false) {\n back = tr.changes.invertedDesc;\n changes = ChangeSet.empty(state.doc.length);\n }\n else {\n let filtered = tr.changes.filter(result);\n changes = filtered.changes;\n back = filtered.filtered.mapDesc(filtered.changes).invertedDesc;\n }\n tr = Transaction.create(state, changes, tr.selection && tr.selection.map(back), StateEffect.mapEffects(tr.effects, back), tr.annotations, tr.scrollIntoView);\n }\n // Transaction filters\n let filters = state.facet(transactionFilter);\n for (let i = filters.length - 1; i >= 0; i--) {\n let filtered = filters[i](tr);\n if (filtered instanceof Transaction)\n tr = filtered;\n else if (Array.isArray(filtered) && filtered.length == 1 && filtered[0] instanceof Transaction)\n tr = filtered[0];\n else\n tr = resolveTransaction(state, asArray(filtered), false);\n }\n return tr;\n}\nfunction extendTransaction(tr) {\n let state = tr.startState, extenders = state.facet(transactionExtender), spec = tr;\n for (let i = extenders.length - 1; i >= 0; i--) {\n let extension = extenders[i](tr);\n if (extension && Object.keys(extension).length)\n spec = mergeTransaction(spec, resolveTransactionInner(state, extension, tr.changes.newLength), true);\n }\n return spec == tr ? tr : Transaction.create(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView);\n}\nconst none = [];\nfunction asArray(value) {\n return value == null ? none : Array.isArray(value) ? value : [value];\n}\n\n/**\nThe categories produced by a [character\ncategorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used\ndo things like selecting by word.\n*/\nvar CharCategory = /*@__PURE__*/(function (CharCategory) {\n /**\n Word characters.\n */\n CharCategory[CharCategory[\"Word\"] = 0] = \"Word\";\n /**\n Whitespace.\n */\n CharCategory[CharCategory[\"Space\"] = 1] = \"Space\";\n /**\n Anything else.\n */\n CharCategory[CharCategory[\"Other\"] = 2] = \"Other\";\nreturn CharCategory})(CharCategory || (CharCategory = {}));\nconst nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;\nlet wordChar;\ntry {\n wordChar = /*@__PURE__*/new RegExp(\"[\\\\p{Alphabetic}\\\\p{Number}_]\", \"u\");\n}\ncatch (_) { }\nfunction hasWordChar(str) {\n if (wordChar)\n return wordChar.test(str);\n for (let i = 0; i < str.length; i++) {\n let ch = str[i];\n if (/\\w/.test(ch) || ch > \"\\x80\" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)))\n return true;\n }\n return false;\n}\nfunction makeCategorizer(wordChars) {\n return (char) => {\n if (!/\\S/.test(char))\n return CharCategory.Space;\n if (hasWordChar(char))\n return CharCategory.Word;\n for (let i = 0; i < wordChars.length; i++)\n if (char.indexOf(wordChars[i]) > -1)\n return CharCategory.Word;\n return CharCategory.Other;\n };\n}\n\n/**\nThe editor state class is a persistent (immutable) data structure.\nTo update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a\n[transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state\ninstance, without modifying the original object.\n\nAs such, _never_ mutate properties of a state directly. That'll\njust break things.\n*/\nclass EditorState {\n constructor(\n /**\n @internal\n */\n config, \n /**\n The current document.\n */\n doc, \n /**\n The current selection.\n */\n selection, \n /**\n @internal\n */\n values, computeSlot, tr) {\n this.config = config;\n this.doc = doc;\n this.selection = selection;\n this.values = values;\n this.status = config.statusTemplate.slice();\n this.computeSlot = computeSlot;\n // Fill in the computed state immediately, so that further queries\n // for it made during the update return this state\n if (tr)\n tr._state = this;\n for (let i = 0; i < this.config.dynamicSlots.length; i++)\n ensureAddr(this, i << 1);\n this.computeSlot = null;\n }\n field(field, require = true) {\n let addr = this.config.address[field.id];\n if (addr == null) {\n if (require)\n throw new RangeError(\"Field is not present in this state\");\n return undefined;\n }\n ensureAddr(this, addr);\n return getAddr(this, addr);\n }\n /**\n Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this\n state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec)\n can be passed. Unless\n [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the\n [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec\n are assumed to start in the _current_ document (not the document\n produced by previous specs), and its\n [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and\n [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer\n to the document created by its _own_ changes. The resulting\n transaction contains the combined effect of all the different\n specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later\n specs take precedence over earlier ones.\n */\n update(...specs) {\n return resolveTransaction(this, specs, true);\n }\n /**\n @internal\n */\n applyTransaction(tr) {\n let conf = this.config, { base, compartments } = conf;\n for (let effect of tr.effects) {\n if (effect.is(Compartment.reconfigure)) {\n if (conf) {\n compartments = new Map;\n conf.compartments.forEach((val, key) => compartments.set(key, val));\n conf = null;\n }\n compartments.set(effect.value.compartment, effect.value.extension);\n }\n else if (effect.is(StateEffect.reconfigure)) {\n conf = null;\n base = effect.value;\n }\n else if (effect.is(StateEffect.appendConfig)) {\n conf = null;\n base = asArray(base).concat(effect.value);\n }\n }\n let startValues;\n if (!conf) {\n conf = Configuration.resolve(base, compartments, this);\n let intermediateState = new EditorState(conf, this.doc, this.selection, conf.dynamicSlots.map(() => null), (state, slot) => slot.reconfigure(state, this), null);\n startValues = intermediateState.values;\n }\n else {\n startValues = tr.startState.values.slice();\n }\n let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle();\n new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr);\n }\n /**\n Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that\n replaces every selection range with the given content.\n */\n replaceSelection(text) {\n if (typeof text == \"string\")\n text = this.toText(text);\n return this.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: text },\n range: EditorSelection.cursor(range.from + text.length) }));\n }\n /**\n Create a set of changes and a new selection by running the given\n function for each range in the active selection. The function\n can return an optional set of changes (in the coordinate space\n of the start document), plus an updated range (in the coordinate\n space of the document produced by the call's own changes). This\n method will merge all the changes and ranges into a single\n changeset and selection, and return it as a [transaction\n spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to\n [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update).\n */\n changeByRange(f) {\n let sel = this.selection;\n let result1 = f(sel.ranges[0]);\n let changes = this.changes(result1.changes), ranges = [result1.range];\n let effects = asArray(result1.effects);\n for (let i = 1; i < sel.ranges.length; i++) {\n let result = f(sel.ranges[i]);\n let newChanges = this.changes(result.changes), newMapped = newChanges.map(changes);\n for (let j = 0; j < i; j++)\n ranges[j] = ranges[j].map(newMapped);\n let mapBy = changes.mapDesc(newChanges, true);\n ranges.push(result.range.map(mapBy));\n changes = changes.compose(newMapped);\n effects = StateEffect.mapEffects(effects, newMapped).concat(StateEffect.mapEffects(asArray(result.effects), mapBy));\n }\n return {\n changes,\n selection: EditorSelection.create(ranges, sel.mainIndex),\n effects\n };\n }\n /**\n Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change\n description, taking the state's document length and line\n separator into account.\n */\n changes(spec = []) {\n if (spec instanceof ChangeSet)\n return spec;\n return ChangeSet.of(spec, this.doc.length, this.facet(EditorState.lineSeparator));\n }\n /**\n Using the state's [line\n separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a\n [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string.\n */\n toText(string) {\n return Text.of(string.split(this.facet(EditorState.lineSeparator) || DefaultSplit));\n }\n /**\n Return the given range of the document as a string.\n */\n sliceDoc(from = 0, to = this.doc.length) {\n return this.doc.sliceString(from, to, this.lineBreak);\n }\n /**\n Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet).\n */\n facet(facet) {\n let addr = this.config.address[facet.id];\n if (addr == null)\n return facet.default;\n ensureAddr(this, addr);\n return getAddr(this, addr);\n }\n /**\n Convert this state to a JSON-serializable object. When custom\n fields should be serialized, you can pass them in as an object\n mapping property names (in the resulting object, which should\n not use `doc` or `selection`) to fields.\n */\n toJSON(fields) {\n let result = {\n doc: this.sliceDoc(),\n selection: this.selection.toJSON()\n };\n if (fields)\n for (let prop in fields) {\n let value = fields[prop];\n if (value instanceof StateField && this.config.address[value.id] != null)\n result[prop] = value.spec.toJSON(this.field(fields[prop]), this);\n }\n return result;\n }\n /**\n Deserialize a state from its JSON representation. When custom\n fields should be deserialized, pass the same object you passed\n to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as\n third argument.\n */\n static fromJSON(json, config = {}, fields) {\n if (!json || typeof json.doc != \"string\")\n throw new RangeError(\"Invalid JSON representation for EditorState\");\n let fieldInit = [];\n if (fields)\n for (let prop in fields) {\n if (Object.prototype.hasOwnProperty.call(json, prop)) {\n let field = fields[prop], value = json[prop];\n fieldInit.push(field.init(state => field.spec.fromJSON(value, state)));\n }\n }\n return EditorState.create({\n doc: json.doc,\n selection: EditorSelection.fromJSON(json.selection),\n extensions: config.extensions ? fieldInit.concat([config.extensions]) : fieldInit\n });\n }\n /**\n Create a new state. You'll usually only need this when\n initializing an editor\u2014updated states are created by applying\n transactions.\n */\n static create(config = {}) {\n let configuration = Configuration.resolve(config.extensions || [], new Map);\n let doc = config.doc instanceof Text ? config.doc\n : Text.of((config.doc || \"\").split(configuration.staticFacet(EditorState.lineSeparator) || DefaultSplit));\n let selection = !config.selection ? EditorSelection.single(0)\n : config.selection instanceof EditorSelection ? config.selection\n : EditorSelection.single(config.selection.anchor, config.selection.head);\n checkSelection(selection, doc.length);\n if (!configuration.staticFacet(allowMultipleSelections))\n selection = selection.asSingle();\n return new EditorState(configuration, doc, selection, configuration.dynamicSlots.map(() => null), (state, slot) => slot.create(state), null);\n }\n /**\n The size (in columns) of a tab in the document, determined by\n the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet.\n */\n get tabSize() { return this.facet(EditorState.tabSize); }\n /**\n Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)\n string for this state.\n */\n get lineBreak() { return this.facet(EditorState.lineSeparator) || \"\\n\"; }\n /**\n Returns true when the editor is\n [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only.\n */\n get readOnly() { return this.facet(readOnly); }\n /**\n Look up a translation for the given phrase (via the\n [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the\n original string if no translation is found.\n \n If additional arguments are passed, they will be inserted in\n place of markers like `$1` (for the first value) and `$2`, etc.\n A single `$` is equivalent to `$1`, and `$$` will produce a\n literal dollar sign.\n */\n phrase(phrase, ...insert) {\n for (let map of this.facet(EditorState.phrases))\n if (Object.prototype.hasOwnProperty.call(map, phrase)) {\n phrase = map[phrase];\n break;\n }\n if (insert.length)\n phrase = phrase.replace(/\\$(\\$|\\d*)/g, (m, i) => {\n if (i == \"$\")\n return \"$\";\n let n = +(i || 1);\n return !n || n > insert.length ? m : insert[n - 1];\n });\n return phrase;\n }\n /**\n Find the values for a given language data field, provided by the\n the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet.\n \n Examples of language data fields are...\n \n - [`\"commentTokens\"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying\n comment syntax.\n - [`\"autocomplete\"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override)\n for providing language-specific completion sources.\n - [`\"wordChars\"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding\n characters that should be considered part of words in this\n language.\n - [`\"closeBrackets\"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls\n bracket closing behavior.\n */\n languageDataAt(name, pos, side = -1) {\n let values = [];\n for (let provider of this.facet(languageData)) {\n for (let result of provider(this, pos, side)) {\n if (Object.prototype.hasOwnProperty.call(result, name))\n values.push(result[name]);\n }\n }\n return values;\n }\n /**\n Return a function that can categorize strings (expected to\n represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak))\n into one of:\n \n - Word (contains an alphanumeric character or a character\n explicitly listed in the local language's `\"wordChars\"`\n language data, which should be a string)\n - Space (contains only whitespace)\n - Other (anything else)\n */\n charCategorizer(at) {\n return makeCategorizer(this.languageDataAt(\"wordChars\", at).join(\"\"));\n }\n /**\n Find the word at the given position, meaning the range\n containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters\n around it. If no word characters are adjacent to the position,\n this returns null.\n */\n wordAt(pos) {\n let { text, from, length } = this.doc.lineAt(pos);\n let cat = this.charCategorizer(pos);\n let start = pos - from, end = pos - from;\n while (start > 0) {\n let prev = findClusterBreak(text, start, false);\n if (cat(text.slice(prev, start)) != CharCategory.Word)\n break;\n start = prev;\n }\n while (end < length) {\n let next = findClusterBreak(text, end);\n if (cat(text.slice(end, next)) != CharCategory.Word)\n break;\n end = next;\n }\n return start == end ? null : EditorSelection.range(start + from, end + from);\n }\n}\n/**\nA facet that, when enabled, causes the editor to allow multiple\nranges to be selected. Be careful though, because by default the\neditor relies on the native DOM selection, which cannot handle\nmultiple selections. An extension like\n[`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make\nsecondary selections visible to the user.\n*/\nEditorState.allowMultipleSelections = allowMultipleSelections;\n/**\nConfigures the tab size to use in this state. The first\n(highest-precedence) value of the facet is used. If no value is\ngiven, this defaults to 4.\n*/\nEditorState.tabSize = /*@__PURE__*/Facet.define({\n combine: values => values.length ? values[0] : 4\n});\n/**\nThe line separator to use. By default, any of `\"\\n\"`, `\"\\r\\n\"`\nand `\"\\r\"` is treated as a separator when splitting lines, and\nlines are joined with `\"\\n\"`.\n\nWhen you configure a value here, only that precise separator\nwill be used, allowing you to round-trip documents through the\neditor without normalizing line separators.\n*/\nEditorState.lineSeparator = lineSeparator;\n/**\nThis facet controls the value of the\n[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is\nconsulted by commands and extensions that implement editing\nfunctionality to determine whether they should apply. It\ndefaults to false, but when its highest-precedence value is\n`true`, such functionality disables itself.\n\nNot to be confused with\n[`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which\ncontrols whether the editor's DOM is set to be editable (and\nthus focusable).\n*/\nEditorState.readOnly = readOnly;\n/**\nRegisters translation phrases. The\n[`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through\nall objects registered with this facet to find translations for\nits argument.\n*/\nEditorState.phrases = /*@__PURE__*/Facet.define({\n compare(a, b) {\n let kA = Object.keys(a), kB = Object.keys(b);\n return kA.length == kB.length && kA.every(k => a[k] == b[k]);\n }\n});\n/**\nA facet used to register [language\ndata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers.\n*/\nEditorState.languageData = languageData;\n/**\nFacet used to register change filters, which are called for each\ntransaction (unless explicitly\n[disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress\npart of the transaction's changes.\n\nSuch a function can return `true` to indicate that it doesn't\nwant to do anything, `false` to completely stop the changes in\nthe transaction, or a set of ranges in which changes should be\nsuppressed. Such ranges are represented as an array of numbers,\nwith each pair of two numbers indicating the start and end of a\nrange. So for example `[10, 20, 100, 110]` suppresses changes\nbetween 10 and 20, and between 100 and 110.\n*/\nEditorState.changeFilter = changeFilter;\n/**\nFacet used to register a hook that gets a chance to update or\nreplace transaction specs before they are applied. This will\nonly be applied for transactions that don't have\n[`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You\ncan either return a single transaction spec (possibly the input\ntransaction), or an array of specs (which will be combined in\nthe same way as the arguments to\n[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)).\n\nWhen possible, it is recommended to avoid accessing\n[`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter,\nsince it will force creation of a state that will then be\ndiscarded again, if the transaction is actually filtered.\n\n(This functionality should be used with care. Indiscriminately\nmodifying transaction is likely to break something or degrade\nthe user experience.)\n*/\nEditorState.transactionFilter = transactionFilter;\n/**\nThis is a more limited form of\n[`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter),\nwhich can only add\n[annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and\n[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type\nof filter runs even if the transaction has disabled regular\n[filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable\nfor effects that don't need to touch the changes or selection,\nbut do want to process every transaction.\n\nExtenders run _after_ filters, when both are present.\n*/\nEditorState.transactionExtender = transactionExtender;\nCompartment.reconfigure = /*@__PURE__*/StateEffect.define();\n\n/**\nUtility function for combining behaviors to fill in a config\nobject from an array of provided configs. `defaults` should hold\ndefault values for all optional fields in `Config`.\n\nThe function will, by default, error\nwhen a field gets two values that aren't `===`-equal, but you can\nprovide combine functions per field to do something else.\n*/\nfunction combineConfig(configs, defaults, // Should hold only the optional properties of Config, but I haven't managed to express that\ncombine = {}) {\n let result = {};\n for (let config of configs)\n for (let key of Object.keys(config)) {\n let value = config[key], current = result[key];\n if (current === undefined)\n result[key] = value;\n else if (current === value || value === undefined) ; // No conflict\n else if (Object.hasOwnProperty.call(combine, key))\n result[key] = combine[key](current, value);\n else\n throw new Error(\"Config merge conflict for field \" + key);\n }\n for (let key in defaults)\n if (result[key] === undefined)\n result[key] = defaults[key];\n return result;\n}\n\n/**\nEach range is associated with a value, which must inherit from\nthis class.\n*/\nclass RangeValue {\n /**\n Compare this value with another value. Used when comparing\n rangesets. The default implementation compares by identity.\n Unless you are only creating a fixed number of unique instances\n of your value type, it is a good idea to implement this\n properly.\n */\n eq(other) { return this == other; }\n /**\n Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value.\n */\n range(from, to = from) { return Range.create(from, to, this); }\n}\nRangeValue.prototype.startSide = RangeValue.prototype.endSide = 0;\nRangeValue.prototype.point = false;\nRangeValue.prototype.mapMode = MapMode.TrackDel;\n/**\nA range associates a value with a range of positions.\n*/\nclass Range {\n constructor(\n /**\n The range's start position.\n */\n from, \n /**\n Its end position.\n */\n to, \n /**\n The value associated with this range.\n */\n value) {\n this.from = from;\n this.to = to;\n this.value = value;\n }\n /**\n @internal\n */\n static create(from, to, value) {\n return new Range(from, to, value);\n }\n}\nfunction cmpRange(a, b) {\n return a.from - b.from || a.value.startSide - b.value.startSide;\n}\nclass Chunk {\n constructor(from, to, value, \n // Chunks are marked with the largest point that occurs\n // in them (or -1 for no points), so that scans that are\n // only interested in points (such as the\n // heightmap-related logic) can skip range-only chunks.\n maxPoint) {\n this.from = from;\n this.to = to;\n this.value = value;\n this.maxPoint = maxPoint;\n }\n get length() { return this.to[this.to.length - 1]; }\n // Find the index of the given position and side. Use the ranges'\n // `from` pos when `end == false`, `to` when `end == true`.\n findIndex(pos, side, end, startAt = 0) {\n let arr = end ? this.to : this.from;\n for (let lo = startAt, hi = arr.length;;) {\n if (lo == hi)\n return lo;\n let mid = (lo + hi) >> 1;\n let diff = arr[mid] - pos || (end ? this.value[mid].endSide : this.value[mid].startSide) - side;\n if (mid == lo)\n return diff >= 0 ? lo : hi;\n if (diff >= 0)\n hi = mid;\n else\n lo = mid + 1;\n }\n }\n between(offset, from, to, f) {\n for (let i = this.findIndex(from, -1000000000 /* C.Far */, true), e = this.findIndex(to, 1000000000 /* C.Far */, false, i); i < e; i++)\n if (f(this.from[i] + offset, this.to[i] + offset, this.value[i]) === false)\n return false;\n }\n map(offset, changes) {\n let value = [], from = [], to = [], newPos = -1, maxPoint = -1;\n for (let i = 0; i < this.value.length; i++) {\n let val = this.value[i], curFrom = this.from[i] + offset, curTo = this.to[i] + offset, newFrom, newTo;\n if (curFrom == curTo) {\n let mapped = changes.mapPos(curFrom, val.startSide, val.mapMode);\n if (mapped == null)\n continue;\n newFrom = newTo = mapped;\n if (val.startSide != val.endSide) {\n newTo = changes.mapPos(curFrom, val.endSide);\n if (newTo < newFrom)\n continue;\n }\n }\n else {\n newFrom = changes.mapPos(curFrom, val.startSide);\n newTo = changes.mapPos(curTo, val.endSide);\n if (newFrom > newTo || newFrom == newTo && val.startSide > 0 && val.endSide <= 0)\n continue;\n }\n if ((newTo - newFrom || val.endSide - val.startSide) < 0)\n continue;\n if (newPos < 0)\n newPos = newFrom;\n if (val.point)\n maxPoint = Math.max(maxPoint, newTo - newFrom);\n value.push(val);\n from.push(newFrom - newPos);\n to.push(newTo - newPos);\n }\n return { mapped: value.length ? new Chunk(from, to, value, maxPoint) : null, pos: newPos };\n }\n}\n/**\nA range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a\nway that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and\n[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data\nstructure.\n*/\nclass RangeSet {\n constructor(\n /**\n @internal\n */\n chunkPos, \n /**\n @internal\n */\n chunk, \n /**\n @internal\n */\n nextLayer, \n /**\n @internal\n */\n maxPoint) {\n this.chunkPos = chunkPos;\n this.chunk = chunk;\n this.nextLayer = nextLayer;\n this.maxPoint = maxPoint;\n }\n /**\n @internal\n */\n static create(chunkPos, chunk, nextLayer, maxPoint) {\n return new RangeSet(chunkPos, chunk, nextLayer, maxPoint);\n }\n /**\n @internal\n */\n get length() {\n let last = this.chunk.length - 1;\n return last < 0 ? 0 : Math.max(this.chunkEnd(last), this.nextLayer.length);\n }\n /**\n The number of ranges in the set.\n */\n get size() {\n if (this.isEmpty)\n return 0;\n let size = this.nextLayer.size;\n for (let chunk of this.chunk)\n size += chunk.value.length;\n return size;\n }\n /**\n @internal\n */\n chunkEnd(index) {\n return this.chunkPos[index] + this.chunk[index].length;\n }\n /**\n Update the range set, optionally adding new ranges or filtering\n out existing ones.\n \n (Note: The type parameter is just there as a kludge to work\n around TypeScript variance issues that prevented `RangeSet`\n from being a subtype of `RangeSet` when `X` is a subtype of\n `Y`.)\n */\n update(updateSpec) {\n let { add = [], sort = false, filterFrom = 0, filterTo = this.length } = updateSpec;\n let filter = updateSpec.filter;\n if (add.length == 0 && !filter)\n return this;\n if (sort)\n add = add.slice().sort(cmpRange);\n if (this.isEmpty)\n return add.length ? RangeSet.of(add) : this;\n let cur = new LayerCursor(this, null, -1).goto(0), i = 0, spill = [];\n let builder = new RangeSetBuilder();\n while (cur.value || i < add.length) {\n if (i < add.length && (cur.from - add[i].from || cur.startSide - add[i].value.startSide) >= 0) {\n let range = add[i++];\n if (!builder.addInner(range.from, range.to, range.value))\n spill.push(range);\n }\n else if (cur.rangeIndex == 1 && cur.chunkIndex < this.chunk.length &&\n (i == add.length || this.chunkEnd(cur.chunkIndex) < add[i].from) &&\n (!filter || filterFrom > this.chunkEnd(cur.chunkIndex) || filterTo < this.chunkPos[cur.chunkIndex]) &&\n builder.addChunk(this.chunkPos[cur.chunkIndex], this.chunk[cur.chunkIndex])) {\n cur.nextChunk();\n }\n else {\n if (!filter || filterFrom > cur.to || filterTo < cur.from || filter(cur.from, cur.to, cur.value)) {\n if (!builder.addInner(cur.from, cur.to, cur.value))\n spill.push(Range.create(cur.from, cur.to, cur.value));\n }\n cur.next();\n }\n }\n return builder.finishInner(this.nextLayer.isEmpty && !spill.length ? RangeSet.empty\n : this.nextLayer.update({ add: spill, filter, filterFrom, filterTo }));\n }\n /**\n Map this range set through a set of changes, return the new set.\n */\n map(changes) {\n if (changes.empty || this.isEmpty)\n return this;\n let chunks = [], chunkPos = [], maxPoint = -1;\n for (let i = 0; i < this.chunk.length; i++) {\n let start = this.chunkPos[i], chunk = this.chunk[i];\n let touch = changes.touchesRange(start, start + chunk.length);\n if (touch === false) {\n maxPoint = Math.max(maxPoint, chunk.maxPoint);\n chunks.push(chunk);\n chunkPos.push(changes.mapPos(start));\n }\n else if (touch === true) {\n let { mapped, pos } = chunk.map(start, changes);\n if (mapped) {\n maxPoint = Math.max(maxPoint, mapped.maxPoint);\n chunks.push(mapped);\n chunkPos.push(pos);\n }\n }\n }\n let next = this.nextLayer.map(changes);\n return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next || RangeSet.empty, maxPoint);\n }\n /**\n Iterate over the ranges that touch the region `from` to `to`,\n calling `f` for each. There is no guarantee that the ranges will\n be reported in any specific order. When the callback returns\n `false`, iteration stops.\n */\n between(from, to, f) {\n if (this.isEmpty)\n return;\n for (let i = 0; i < this.chunk.length; i++) {\n let start = this.chunkPos[i], chunk = this.chunk[i];\n if (to >= start && from <= start + chunk.length &&\n chunk.between(start, from - start, to - start, f) === false)\n return;\n }\n this.nextLayer.between(from, to, f);\n }\n /**\n Iterate over the ranges in this set, in order, including all\n ranges that end at or after `from`.\n */\n iter(from = 0) {\n return HeapCursor.from([this]).goto(from);\n }\n /**\n @internal\n */\n get isEmpty() { return this.nextLayer == this; }\n /**\n Iterate over the ranges in a collection of sets, in order,\n starting from `from`.\n */\n static iter(sets, from = 0) {\n return HeapCursor.from(sets).goto(from);\n }\n /**\n Iterate over two groups of sets, calling methods on `comparator`\n to notify it of possible differences.\n */\n static compare(oldSets, newSets, \n /**\n This indicates how the underlying data changed between these\n ranges, and is needed to synchronize the iteration.\n */\n textDiff, comparator, \n /**\n Can be used to ignore all non-point ranges, and points below\n the given size. When -1, all ranges are compared.\n */\n minPointSize = -1) {\n let a = oldSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize);\n let b = newSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize);\n let sharedChunks = findSharedChunks(a, b, textDiff);\n let sideA = new SpanCursor(a, sharedChunks, minPointSize);\n let sideB = new SpanCursor(b, sharedChunks, minPointSize);\n textDiff.iterGaps((fromA, fromB, length) => compare(sideA, fromA, sideB, fromB, length, comparator));\n if (textDiff.empty && textDiff.length == 0)\n compare(sideA, 0, sideB, 0, 0, comparator);\n }\n /**\n Compare the contents of two groups of range sets, returning true\n if they are equivalent in the given range.\n */\n static eq(oldSets, newSets, from = 0, to) {\n if (to == null)\n to = 1000000000 /* C.Far */ - 1;\n let a = oldSets.filter(set => !set.isEmpty && newSets.indexOf(set) < 0);\n let b = newSets.filter(set => !set.isEmpty && oldSets.indexOf(set) < 0);\n if (a.length != b.length)\n return false;\n if (!a.length)\n return true;\n let sharedChunks = findSharedChunks(a, b);\n let sideA = new SpanCursor(a, sharedChunks, 0).goto(from), sideB = new SpanCursor(b, sharedChunks, 0).goto(from);\n for (;;) {\n if (sideA.to != sideB.to ||\n !sameValues(sideA.active, sideB.active) ||\n sideA.point && (!sideB.point || !sideA.point.eq(sideB.point)))\n return false;\n if (sideA.to > to)\n return true;\n sideA.next();\n sideB.next();\n }\n }\n /**\n Iterate over a group of range sets at the same time, notifying\n the iterator about the ranges covering every given piece of\n content. Returns the open count (see\n [`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end\n of the iteration.\n */\n static spans(sets, from, to, iterator, \n /**\n When given and greater than -1, only points of at least this\n size are taken into account.\n */\n minPointSize = -1) {\n let cursor = new SpanCursor(sets, null, minPointSize).goto(from), pos = from;\n let openRanges = cursor.openStart;\n for (;;) {\n let curTo = Math.min(cursor.to, to);\n if (cursor.point) {\n let active = cursor.activeForPoint(cursor.to);\n let openCount = cursor.pointFrom < from ? active.length + 1 : Math.min(active.length, openRanges);\n iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank);\n openRanges = Math.min(cursor.openEnd(curTo), active.length);\n }\n else if (curTo > pos) {\n iterator.span(pos, curTo, cursor.active, openRanges);\n openRanges = cursor.openEnd(curTo);\n }\n if (cursor.to > to)\n return openRanges + (cursor.point && cursor.to > to ? 1 : 0);\n pos = cursor.to;\n cursor.next();\n }\n }\n /**\n Create a range set for the given range or array of ranges. By\n default, this expects the ranges to be _sorted_ (by start\n position and, if two start at the same position,\n `value.startSide`). You can pass `true` as second argument to\n cause the method to sort them.\n */\n static of(ranges, sort = false) {\n let build = new RangeSetBuilder();\n for (let range of ranges instanceof Range ? [ranges] : sort ? lazySort(ranges) : ranges)\n build.add(range.from, range.to, range.value);\n return build.finish();\n }\n /**\n Join an array of range sets into a single set.\n */\n static join(sets) {\n if (!sets.length)\n return RangeSet.empty;\n let result = sets[sets.length - 1];\n for (let i = sets.length - 2; i >= 0; i--) {\n for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)\n result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));\n }\n return result;\n }\n}\n/**\nThe empty set of ranges.\n*/\nRangeSet.empty = /*@__PURE__*/new RangeSet([], [], null, -1);\nfunction lazySort(ranges) {\n if (ranges.length > 1)\n for (let prev = ranges[0], i = 1; i < ranges.length; i++) {\n let cur = ranges[i];\n if (cmpRange(prev, cur) > 0)\n return ranges.slice().sort(cmpRange);\n prev = cur;\n }\n return ranges;\n}\nRangeSet.empty.nextLayer = RangeSet.empty;\n/**\nA range set builder is a data structure that helps build up a\n[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating\nan array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects.\n*/\nclass RangeSetBuilder {\n finishChunk(newArrays) {\n this.chunks.push(new Chunk(this.from, this.to, this.value, this.maxPoint));\n this.chunkPos.push(this.chunkStart);\n this.chunkStart = -1;\n this.setMaxPoint = Math.max(this.setMaxPoint, this.maxPoint);\n this.maxPoint = -1;\n if (newArrays) {\n this.from = [];\n this.to = [];\n this.value = [];\n }\n }\n /**\n Create an empty builder.\n */\n constructor() {\n this.chunks = [];\n this.chunkPos = [];\n this.chunkStart = -1;\n this.last = null;\n this.lastFrom = -1000000000 /* C.Far */;\n this.lastTo = -1000000000 /* C.Far */;\n this.from = [];\n this.to = [];\n this.value = [];\n this.maxPoint = -1;\n this.setMaxPoint = -1;\n this.nextLayer = null;\n }\n /**\n Add a range. Ranges should be added in sorted (by `from` and\n `value.startSide`) order.\n */\n add(from, to, value) {\n if (!this.addInner(from, to, value))\n (this.nextLayer || (this.nextLayer = new RangeSetBuilder)).add(from, to, value);\n }\n /**\n @internal\n */\n addInner(from, to, value) {\n let diff = from - this.lastTo || value.startSide - this.last.endSide;\n if (diff <= 0 && (from - this.lastFrom || value.startSide - this.last.startSide) < 0)\n throw new Error(\"Ranges must be added sorted by `from` position and `startSide`\");\n if (diff < 0)\n return false;\n if (this.from.length == 250 /* C.ChunkSize */)\n this.finishChunk(true);\n if (this.chunkStart < 0)\n this.chunkStart = from;\n this.from.push(from - this.chunkStart);\n this.to.push(to - this.chunkStart);\n this.last = value;\n this.lastFrom = from;\n this.lastTo = to;\n this.value.push(value);\n if (value.point)\n this.maxPoint = Math.max(this.maxPoint, to - from);\n return true;\n }\n /**\n @internal\n */\n addChunk(from, chunk) {\n if ((from - this.lastTo || chunk.value[0].startSide - this.last.endSide) < 0)\n return false;\n if (this.from.length)\n this.finishChunk(true);\n this.setMaxPoint = Math.max(this.setMaxPoint, chunk.maxPoint);\n this.chunks.push(chunk);\n this.chunkPos.push(from);\n let last = chunk.value.length - 1;\n this.last = chunk.value[last];\n this.lastFrom = chunk.from[last] + from;\n this.lastTo = chunk.to[last] + from;\n return true;\n }\n /**\n Finish the range set. Returns the new set. The builder can't be\n used anymore after this has been called.\n */\n finish() { return this.finishInner(RangeSet.empty); }\n /**\n @internal\n */\n finishInner(next) {\n if (this.from.length)\n this.finishChunk(false);\n if (this.chunks.length == 0)\n return next;\n let result = RangeSet.create(this.chunkPos, this.chunks, this.nextLayer ? this.nextLayer.finishInner(next) : next, this.setMaxPoint);\n this.from = null; // Make sure further `add` calls produce errors\n return result;\n }\n}\nfunction findSharedChunks(a, b, textDiff) {\n let inA = new Map();\n for (let set of a)\n for (let i = 0; i < set.chunk.length; i++)\n if (set.chunk[i].maxPoint <= 0)\n inA.set(set.chunk[i], set.chunkPos[i]);\n let shared = new Set();\n for (let set of b)\n for (let i = 0; i < set.chunk.length; i++) {\n let known = inA.get(set.chunk[i]);\n if (known != null && (textDiff ? textDiff.mapPos(known) : known) == set.chunkPos[i] &&\n !(textDiff === null || textDiff === void 0 ? void 0 : textDiff.touchesRange(known, known + set.chunk[i].length)))\n shared.add(set.chunk[i]);\n }\n return shared;\n}\nclass LayerCursor {\n constructor(layer, skip, minPoint, rank = 0) {\n this.layer = layer;\n this.skip = skip;\n this.minPoint = minPoint;\n this.rank = rank;\n }\n get startSide() { return this.value ? this.value.startSide : 0; }\n get endSide() { return this.value ? this.value.endSide : 0; }\n goto(pos, side = -1000000000 /* C.Far */) {\n this.chunkIndex = this.rangeIndex = 0;\n this.gotoInner(pos, side, false);\n return this;\n }\n gotoInner(pos, side, forward) {\n while (this.chunkIndex < this.layer.chunk.length) {\n let next = this.layer.chunk[this.chunkIndex];\n if (!(this.skip && this.skip.has(next) ||\n this.layer.chunkEnd(this.chunkIndex) < pos ||\n next.maxPoint < this.minPoint))\n break;\n this.chunkIndex++;\n forward = false;\n }\n if (this.chunkIndex < this.layer.chunk.length) {\n let rangeIndex = this.layer.chunk[this.chunkIndex].findIndex(pos - this.layer.chunkPos[this.chunkIndex], side, true);\n if (!forward || this.rangeIndex < rangeIndex)\n this.setRangeIndex(rangeIndex);\n }\n this.next();\n }\n forward(pos, side) {\n if ((this.to - pos || this.endSide - side) < 0)\n this.gotoInner(pos, side, true);\n }\n next() {\n for (;;) {\n if (this.chunkIndex == this.layer.chunk.length) {\n this.from = this.to = 1000000000 /* C.Far */;\n this.value = null;\n break;\n }\n else {\n let chunkPos = this.layer.chunkPos[this.chunkIndex], chunk = this.layer.chunk[this.chunkIndex];\n let from = chunkPos + chunk.from[this.rangeIndex];\n this.from = from;\n this.to = chunkPos + chunk.to[this.rangeIndex];\n this.value = chunk.value[this.rangeIndex];\n this.setRangeIndex(this.rangeIndex + 1);\n if (this.minPoint < 0 || this.value.point && this.to - this.from >= this.minPoint)\n break;\n }\n }\n }\n setRangeIndex(index) {\n if (index == this.layer.chunk[this.chunkIndex].value.length) {\n this.chunkIndex++;\n if (this.skip) {\n while (this.chunkIndex < this.layer.chunk.length && this.skip.has(this.layer.chunk[this.chunkIndex]))\n this.chunkIndex++;\n }\n this.rangeIndex = 0;\n }\n else {\n this.rangeIndex = index;\n }\n }\n nextChunk() {\n this.chunkIndex++;\n this.rangeIndex = 0;\n this.next();\n }\n compare(other) {\n return this.from - other.from || this.startSide - other.startSide || this.rank - other.rank ||\n this.to - other.to || this.endSide - other.endSide;\n }\n}\nclass HeapCursor {\n constructor(heap) {\n this.heap = heap;\n }\n static from(sets, skip = null, minPoint = -1) {\n let heap = [];\n for (let i = 0; i < sets.length; i++) {\n for (let cur = sets[i]; !cur.isEmpty; cur = cur.nextLayer) {\n if (cur.maxPoint >= minPoint)\n heap.push(new LayerCursor(cur, skip, minPoint, i));\n }\n }\n return heap.length == 1 ? heap[0] : new HeapCursor(heap);\n }\n get startSide() { return this.value ? this.value.startSide : 0; }\n goto(pos, side = -1000000000 /* C.Far */) {\n for (let cur of this.heap)\n cur.goto(pos, side);\n for (let i = this.heap.length >> 1; i >= 0; i--)\n heapBubble(this.heap, i);\n this.next();\n return this;\n }\n forward(pos, side) {\n for (let cur of this.heap)\n cur.forward(pos, side);\n for (let i = this.heap.length >> 1; i >= 0; i--)\n heapBubble(this.heap, i);\n if ((this.to - pos || this.value.endSide - side) < 0)\n this.next();\n }\n next() {\n if (this.heap.length == 0) {\n this.from = this.to = 1000000000 /* C.Far */;\n this.value = null;\n this.rank = -1;\n }\n else {\n let top = this.heap[0];\n this.from = top.from;\n this.to = top.to;\n this.value = top.value;\n this.rank = top.rank;\n if (top.value)\n top.next();\n heapBubble(this.heap, 0);\n }\n }\n}\nfunction heapBubble(heap, index) {\n for (let cur = heap[index];;) {\n let childIndex = (index << 1) + 1;\n if (childIndex >= heap.length)\n break;\n let child = heap[childIndex];\n if (childIndex + 1 < heap.length && child.compare(heap[childIndex + 1]) >= 0) {\n child = heap[childIndex + 1];\n childIndex++;\n }\n if (cur.compare(child) < 0)\n break;\n heap[childIndex] = cur;\n heap[index] = child;\n index = childIndex;\n }\n}\nclass SpanCursor {\n constructor(sets, skip, minPoint) {\n this.minPoint = minPoint;\n this.active = [];\n this.activeTo = [];\n this.activeRank = [];\n this.minActive = -1;\n // A currently active point range, if any\n this.point = null;\n this.pointFrom = 0;\n this.pointRank = 0;\n this.to = -1000000000 /* C.Far */;\n this.endSide = 0;\n // The amount of open active ranges at the start of the iterator.\n // Not including points.\n this.openStart = -1;\n this.cursor = HeapCursor.from(sets, skip, minPoint);\n }\n goto(pos, side = -1000000000 /* C.Far */) {\n this.cursor.goto(pos, side);\n this.active.length = this.activeTo.length = this.activeRank.length = 0;\n this.minActive = -1;\n this.to = pos;\n this.endSide = side;\n this.openStart = -1;\n this.next();\n return this;\n }\n forward(pos, side) {\n while (this.minActive > -1 && (this.activeTo[this.minActive] - pos || this.active[this.minActive].endSide - side) < 0)\n this.removeActive(this.minActive);\n this.cursor.forward(pos, side);\n }\n removeActive(index) {\n remove(this.active, index);\n remove(this.activeTo, index);\n remove(this.activeRank, index);\n this.minActive = findMinIndex(this.active, this.activeTo);\n }\n addActive(trackOpen) {\n let i = 0, { value, to, rank } = this.cursor;\n // Organize active marks by rank first, then by size\n while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0)\n i++;\n insert(this.active, i, value);\n insert(this.activeTo, i, to);\n insert(this.activeRank, i, rank);\n if (trackOpen)\n insert(trackOpen, i, this.cursor.from);\n this.minActive = findMinIndex(this.active, this.activeTo);\n }\n // After calling this, if `this.point` != null, the next range is a\n // point. Otherwise, it's a regular range, covered by `this.active`.\n next() {\n let from = this.to, wasPoint = this.point;\n this.point = null;\n let trackOpen = this.openStart < 0 ? [] : null;\n for (;;) {\n let a = this.minActive;\n if (a > -1 && (this.activeTo[a] - this.cursor.from || this.active[a].endSide - this.cursor.startSide) < 0) {\n if (this.activeTo[a] > from) {\n this.to = this.activeTo[a];\n this.endSide = this.active[a].endSide;\n break;\n }\n this.removeActive(a);\n if (trackOpen)\n remove(trackOpen, a);\n }\n else if (!this.cursor.value) {\n this.to = this.endSide = 1000000000 /* C.Far */;\n break;\n }\n else if (this.cursor.from > from) {\n this.to = this.cursor.from;\n this.endSide = this.cursor.startSide;\n break;\n }\n else {\n let nextVal = this.cursor.value;\n if (!nextVal.point) { // Opening a range\n this.addActive(trackOpen);\n this.cursor.next();\n }\n else if (wasPoint && this.cursor.to == this.to && this.cursor.from < this.cursor.to) {\n // Ignore any non-empty points that end precisely at the end of the prev point\n this.cursor.next();\n }\n else { // New point\n this.point = nextVal;\n this.pointFrom = this.cursor.from;\n this.pointRank = this.cursor.rank;\n this.to = this.cursor.to;\n this.endSide = nextVal.endSide;\n this.cursor.next();\n this.forward(this.to, this.endSide);\n break;\n }\n }\n }\n if (trackOpen) {\n this.openStart = 0;\n for (let i = trackOpen.length - 1; i >= 0 && trackOpen[i] < from; i--)\n this.openStart++;\n }\n }\n activeForPoint(to) {\n if (!this.active.length)\n return this.active;\n let active = [];\n for (let i = this.active.length - 1; i >= 0; i--) {\n if (this.activeRank[i] < this.pointRank)\n break;\n if (this.activeTo[i] > to || this.activeTo[i] == to && this.active[i].endSide >= this.point.endSide)\n active.push(this.active[i]);\n }\n return active.reverse();\n }\n openEnd(to) {\n let open = 0;\n for (let i = this.activeTo.length - 1; i >= 0 && this.activeTo[i] > to; i--)\n open++;\n return open;\n }\n}\nfunction compare(a, startA, b, startB, length, comparator) {\n a.goto(startA);\n b.goto(startB);\n let endB = startB + length;\n let pos = startB, dPos = startB - startA;\n for (;;) {\n let diff = (a.to + dPos) - b.to || a.endSide - b.endSide;\n let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB);\n if (a.point || b.point) {\n if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) &&\n sameValues(a.activeForPoint(a.to), b.activeForPoint(b.to))))\n comparator.comparePoint(pos, clipEnd, a.point, b.point);\n }\n else {\n if (clipEnd > pos && !sameValues(a.active, b.active))\n comparator.compareRange(pos, clipEnd, a.active, b.active);\n }\n if (end > endB)\n break;\n pos = end;\n if (diff <= 0)\n a.next();\n if (diff >= 0)\n b.next();\n }\n}\nfunction sameValues(a, b) {\n if (a.length != b.length)\n return false;\n for (let i = 0; i < a.length; i++)\n if (a[i] != b[i] && !a[i].eq(b[i]))\n return false;\n return true;\n}\nfunction remove(array, index) {\n for (let i = index, e = array.length - 1; i < e; i++)\n array[i] = array[i + 1];\n array.pop();\n}\nfunction insert(array, index, value) {\n for (let i = array.length - 1; i >= index; i--)\n array[i + 1] = array[i];\n array[index] = value;\n}\nfunction findMinIndex(value, array) {\n let found = -1, foundPos = 1000000000 /* C.Far */;\n for (let i = 0; i < array.length; i++)\n if ((array[i] - foundPos || value[i].endSide - value[found].endSide) < 0) {\n found = i;\n foundPos = array[i];\n }\n return found;\n}\n\n/**\nCount the column position at the given offset into the string,\ntaking extending characters and tab size into account.\n*/\nfunction countColumn(string, tabSize, to = string.length) {\n let n = 0;\n for (let i = 0; i < to;) {\n if (string.charCodeAt(i) == 9) {\n n += tabSize - (n % tabSize);\n i++;\n }\n else {\n n++;\n i = findClusterBreak(string, i);\n }\n }\n return n;\n}\n/**\nFind the offset that corresponds to the given column position in a\nstring, taking extending characters and tab size into account. By\ndefault, the string length is returned when it is too short to\nreach the column. Pass `strict` true to make it return -1 in that\nsituation.\n*/\nfunction findColumn(string, col, tabSize, strict) {\n for (let i = 0, n = 0;;) {\n if (n >= col)\n return i;\n if (i == string.length)\n break;\n n += string.charCodeAt(i) == 9 ? tabSize - (n % tabSize) : 1;\n i = findClusterBreak(string, i);\n }\n return strict === true ? -1 : string.length;\n}\n\nexport { Annotation, AnnotationType, ChangeDesc, ChangeSet, CharCategory, Compartment, EditorSelection, EditorState, Facet, Line, MapMode, Prec, Range, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, StateEffect, StateEffectType, StateField, Text, Transaction, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };\n", "const C = \"\\u037c\"\nconst COUNT = typeof Symbol == \"undefined\" ? \"__\" + C : Symbol.for(C)\nconst SET = typeof Symbol == \"undefined\" ? \"__styleSet\" + Math.floor(Math.random() * 1e8) : Symbol(\"styleSet\")\nconst top = typeof globalThis != \"undefined\" ? globalThis : typeof window != \"undefined\" ? window : {}\n\n// :: - Style modules encapsulate a set of CSS rules defined from\n// JavaScript. Their definitions are only available in a given DOM\n// root after it has been _mounted_ there with `StyleModule.mount`.\n//\n// Style modules should be created once and stored somewhere, as\n// opposed to re-creating them every time you need them. The amount of\n// CSS rules generated for a given DOM root is bounded by the amount\n// of style modules that were used. So to avoid leaking rules, don't\n// create these dynamically, but treat them as one-time allocations.\nexport class StyleModule {\n // :: (Object