[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n*.sln    merge=union\n*.csproj merge=union\n*.vbproj merge=union\n*.fsproj merge=union\n*.dbproj merge=union\n\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\n\nbin/regpack -crlf\n\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n"
  },
  {
    "path": ".gitignore.default",
    "content": "#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.classpath\n.settings/\n.loadpath\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# CDT-specific\n.cproject\n\n# PDT-specific\n.buildpath\n\n\n#################\n## Visual Studio\n#################\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Rr]elease/\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.vspscc\n.builds\n*.dotCover\n\n## TODO: If you have NuGet Package Restore enabled, uncomment this\n#packages/\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n\n# Visual Studio profiler\n*.psess\n*.vsp\n\n# ReSharper is a .NET coding add-in\n_ReSharper*\n\n# Installshield output folder\n[Ee]xpress\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish\n\n# Others\n[Bb]in\n[Oo]bj\nsql\nTestResults\n*.Cache\nClientBin\nstylecop.*\n~$*\n*.dbmdl\nGenerated_Code #added for RIA/Silverlight projects\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\n\n\n\n############\n## Windows\n############\n\n# Windows image file caches\nThumbs.db\n\n# Folder config file\nDesktop.ini\n\n\n#############\n## Python\n#############\n\n*.py[co]\n\n# Packages\n*.egg\n*.egg-info\ndist\nbuild\neggs\nparts\nbin\nvar\nsdist\ndevelop-eggs\n.installed.cfg\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\n.coverage\n.tox\n\n#Translations\n*.mo\n\n#Mr Developer\n.mr.developer.cfg\n\n# Mac crap\n.DS_Store\n"
  },
  {
    "path": ".npmignore",
    "content": "TestCases\ntests\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2012-2016 Siorki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n------------------------------------------------------------------------------\n\nCode produced by RegPack, including the hashing (if included) and unpacking\nroutines, is not affected by this license. No restriction to its usage or\nredistribution arise from its compression by RegPack."
  },
  {
    "path": "README.md",
    "content": "**RegPack** is a packer intended for use on minified Javascript code. Current revision is 5.0.4\n\nIt is mostly suited to small size constraints (1kb, up to 4kb).\n\nThe current version works in seven stages :\n- *(if enabled)* 2D, WebGL and Audio contexts method shortcuts are defined and used instead of the long, original names.\n- *(if enabled)* variables are renamed in order to reduce character footprint and free tokens for compression.\n- *(if enabled)* Initialization code is pushed inside the main loop, with conditional execution on the time variable, so that the unpacker can execute everything through ``setInterval()``\n- string quotes are altered in order to minimize escaping inside the strings\n- redundancies are eliminated and replaced by tokens, as done by JS Crush (by Aivo Paas) and First Crush (by Tim Down).\n- the token list is turned into a regular expression consisting in a character class.\n- the tokens are rearranged to create a negated character class (starting with a hyphen ^ then listing nontoken characters)\n\nThe text boxes show intermediate stage results. Best one gets a green highlight :\n- Preprocessed : after the first four stages. Hidden if no change was brought to the initial code.\n- Crushed : after the fifth stage\n- RegPack'ed : best between last two stages. Depends on how the characters present in the string are spread in the ASCII table.\n\n--\n## Usage tips\n\n- Toggle method hashing for any type of context you use. If the method renaming yields a longer code, RegPack will revert to the original one.\n- \"Assume global .. is a context\" is for environments where the canvas is declared before your code. If entering [js1k](http://www.js1k.com), keep this on, variable is c for classic and g for webgl.\n- Variable renaming is also performed inside strings, RegPack does not infer whether they are eval()ed or not. Disable if facing issues with this. Only one-letter variables are considered, others are ignored.\n- Crusher settings alter the choice of strings to compress. 1/0/0 is a good allrounder, although more exotic values can yield better results depending on your code.\n- Some preprocessing options negatively affect the performance and should be used with caution. Always test your packed code for speed after using these.\n  - \"Encapsulate with(Math)\" get rid of all \"Math.\" references in the code and enclose the evaluation with(Math).\n  - \"run with setInterval()\" executes the unpacked code with ``setInterval()`` instead of ``eval()`` (meaning the evaluation is performed every frame).\n\n[Use it online](http://siorki.github.io/regPack.html) or integrate it into your Node workflow (thanks to kanaka)\n\n## CLI usage\n\n```\nregpack input.js > output.js\nregpack input.js --crushGainFactor 1 --crushLengthFactor 0 --crushCopiesFactor 0 > output.js\n```\n\nFrom STDIN\n\n```\ncat input.js | regpack - > output.js\n```\n\n--\n## Running unit tests with Node\n\n```\ncd tests\nnode AllTests\n```\n\n--\n## License\n\nLicensed under [MIT license](http://opensource.org/licenses/mit-license.html).\n\nCode produced by RegPack, including the hashing (if included) and unpacking routines, is not affected by the license. No restriction to its usage or redistribution arise from its compression by RegPack.  \n\n--\n\nAny feedback or improvement suggestions appreciated. Contributions welcome.\n\n@Siorki on Twitter\n"
  },
  {
    "path": "TestCases/audioContext_create1.js",
    "content": "try {\n\t\t\tthis.webAudioSupport = true;\n\t\t\tif (typeof AudioContext !== \"undefined\") {\n\t\t\t\tthis.audioContext = new AudioContext()\n\t\t\t} else if (typeof webkitAudioContext !== \"undefined\") {\n\t\t\t\tthis.audioContext = new webkitAudioContext()\n\t\t\t} else {\n\t\t\t\tthis.webAudioSupport = false\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.webAudioSupport = false\n\t\t}\n\t}\nthis.audioContext.createBuffer(2,22050,22050);"
  },
  {
    "path": "TestCases/audioContext_create2.js",
    "content": "var stdContext=null, webkitContext=null;\nif (typeof AudioContext !== \"undefined\") {\n\tstdContext = new AudioContext()\n}\nif (typeof webkitAudioContext !== \"undefined\") {\n\twebkitContext = new webkitAudioContext()\n}\nstdContext.createBuffer(1, 22050, 22050);\nstdContext.createGain();\nwebkitContext.createChannelMerger(2);\nvar c=15;\nvar d=7;\n"
  },
  {
    "path": "TestCases/audioContext_create3.js",
    "content": "var stdContext=null, webkitContext=null;\nif (typeof webkitAudioContext !== \"undefined\") {\n\twebkitContext = new webkitAudioContext()\n}\nif (typeof AudioContext !== \"undefined\") {\n\tstdContext = new AudioContext()\n}\nstdContext.createBuffer(1, 22050, 22050);\nstdContext.createGain();\nwebkitContext.createChannelMerger(2);\nvar c=15;\nvar d=7;\n"
  },
  {
    "path": "TestCases/audioContext_create4.js",
    "content": "m=[];s=[0,3,5,7,10];pl=0;for(i=0;16>i;i++)m[i]=0;ac=new webkitAudioContext()||new AudioContext();var nb=ac.createBuffer(1,16384,ac.sampleRate);d=nb.getChannelData(0);for(i=0;16384>i;i++)d[i]=2*Math.random()-1;d=ac.createDelay();d.delayTime.value=60/138;g=ac.createGain();g.gain.value=0.5;c=ac.createDynamicsCompressor();d.connect(g);g.connect(d);g.connect(c);c.connect(ac.destination);setInterval(function(){t=ac.currentTime;n=m[pl];0!=n&&(o=ac.createOscillator(),o.frequency.value=440*Math.pow(2,(n-69)/12),o.type=\"sawtooth\",a=ac.createGain(),g=a.gain,g.setValueAtTime(1,t),g.linearRampToValueAtTime(0,t+1),f=ac.createBiquadFilter(),p=f.frequency,p.setValueAtTime(3E3+2E3*Math.random(),t),p.exponentialRampToValueAtTime(20,t+0.6),f.Q.value=5,o.connect(a),a.connect(f),f.connect(d),o.start(t),o.stop(t+0.5));0==pl%4?(o=ac.createOscillator(),f=o.frequency,f.setValueAtTime(200,t),f.exponentialRampToValueAtTime(10,t+0.25),a=ac.createGain(),g=a.gain,g.setValueAtTime(1.25,t),g.linearRampToValueAtTime(0,t+0.5),o.connect(a),a.connect(c),o.start(t),o.stop(t+0.5)):0==(pl+2)%4?(n=ac.createBufferSource(),n.buffer=nb,n.loop=!0,a=ac.createGain(),g=a.gain,g.setValueAtTime(1.25,t),g.linearRampToValueAtTime(0,t+0.2),f=ac.createBiquadFilter(),p=f.frequency,p.setValueAtTime(2500,t),p.exponentialRampToValueAtTime(500,t+0.1),n.connect(a),a.connect(f),f.connect(c),n.start(t),n.stop(t+1)):(o=ac.createBufferSource(),o.buffer=nb,o.loop=!0,f=ac.createBiquadFilter(),f.type=\"highpass\",f.frequency.value=8E3+2E3*Math.random(),f.Q.value=5,o.connect(f),f.connect(c),o.start(t),o.stop(t+0.02));0==pl&&(i=Math.floor(16*Math.random()),0.1>Math.random()?m[i]=0:(j=Math.floor(Math.random()*s.length),o=Math.floor(Math.random()*Math.random()*3),m[i]=40+s[j]+12*o));pl=(pl+1)%16},60/138*1E3/2);"
  },
  {
    "path": "TestCases/double_renaming.js",
    "content": "a.fillRect(0,0,91,91);\n\nsetInterval( function () {\t\n\tk = 2+2*Math.cos(3.1416*t/64);\n\ti=0;\n\twhile(i<300) {\n\t\ta.strokeStyle = \"hsl(20,0%,\"+(k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140)/4+\"%)\";\n\t\ta.lineWidth=0|(k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140);\n\t\ta.beginPath();\n\t\ta.moveTo(k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140,\n\t\t\t\t k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140);\n\t\ta.lineTo(k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140,\n\t\t\t\t k*x.charCodeAt(i+300)+(4-k)*x.charCodeAt(i++)-140);\n\t\ta.stroke();\n\t}\n\tw=w?--w:(++t&63)?w:64;\t\n\t\t\n}, 40);"
  },
  {
    "path": "TestCases/gitHub#17-multipleContexts.js",
    "content": "a=a.cloneNode(p=[]);cc=a.getContext(\"2d\");cc.fillStyle=n=cc.createRadialGradient(225,75,40,225,75,60);n.addColorStop(t=0,\"#332\");n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1200<1){for(x=y=f=s=0;f<256*256;++f)p[f]=Math.min(0,3*Math.cos((f&255)/40)-4*Math.sin(f/12800)-3);for(n=5e5;--n;s=13*s+2+9*Math.cos(s)|0)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=-6;++i;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(t/200);for(y=-75;++y<75;)for(u=Math.sqrt(75*75-y*y)|0,q=4*((y+75)*150+75-u),x=-u;++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI|0)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?0:128+10*i);d[q++]=n*(i<0?0:64+25*i);d[q++]=n*(i<0?128:10*i);d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(0,0%,\"+Math.max(0,100-t)+\"%)\";c.fillRect(0,0,a.width,a.height);c.save();c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=-400+800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300),500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));c.fillStyle=\"#FFF\";for(i=0;++i-3000;)c.fillRect(i*i%4400-1200,i%1700-1200,2,1);n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=150;c.globalCompositeOperation=\"lighter\";for(i=0;++i-7;)c.drawImage(a,150,0,150,150,-n,-n,2*n,2*n);for(i=0;++i-7;)n=50*i,c.drawImage(a,150,0,150,150,-2*u*i-n,-n,2*n,2*n);c.restore();++t},40);"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationAlone.js",
    "content": "a=a.cloneNode(p=[s=5]);t=0;cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationAtBegin1.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationAtBegin2.js",
    "content": "t=0,a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationAtEnd1.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;t=0;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationAtEnd2.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data,t=0;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationChained1.js",
    "content": "a=a.cloneNode(p=[s=5]);t=u=0;cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationChained2.js",
    "content": "a=a.cloneNode(p=[s=5]);u=t=0;cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationInArray1.js",
    "content": "a=a.cloneNode(p=[t=0,s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationInArray2.js",
    "content": "a=a.cloneNode(p=[s=5,t=0]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationInFunction1.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(t=0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationInFunction2.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,t=0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#19-setInterval_declarationInFunction3.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,t=0);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#2-unicode.js",
    "content": "﻿b.innerHTML=\"<center><p onclick=g(1,3,3,f=1)>XnO<p onclick=g(3,3,3,f=1)>XnO3D<p onclick=g(1,6,7,f=2)>Find4<p onclick=g(1,8,8,f=0)>Reversi<p id=B><p id=C>\";g=function(l,h,n){c=e=1;i=0;d=[];f||(d[27]=d[36]=-1,d[28]=d[35]=1);for(B.innerHTML=\"\";l--;){a=\"<p><table border>\";for(j=h;j--;)for(a+=\"<tr>\",k=n;k--;)d[i]=d[i]||0,a+=\"<th width=20 onclick=m(this,\"+i+\") id=t\"+i+\">\"+\"X\\xa0O\"[d[i++]+1];B.innerHTML+=a}C.innerHTML=\"O\"}; m=function(l,h){if(e&&!d[h]){if(1==f)for(l.innerHTML=\"XnO\"[c+1],a=3*(d[h]=c),i=3;i--;)for(j=3;j--;){if(d[9*i+j]+d[9*i+j+3]+d[9*i+j+6]==a||d[9*i+3*j]+d[9*i+3*j+1]+d[9*i+3*j+2]==a||d[9*i+0]+d[9*i+4]+d[9*i+8]==a||d[9*i+2]+d[9*i+4]+d[9*i+6]==a||d[3*i]+d[3*i+10]+d[3*i+20]==a||d[3*i+2]+d[3*i+10]+d[18+3*i]==a||d[i]+d[9+i+3]+d[18+i+6]==a||d[i+6]+d[9+i+3]+d[18+i]==a||d[0]+d[13]+d[26]==a||d[2]+d[13]+d[24]==a||d[8]+d[13]+d[18]==a||d[6]+d[13]+d[20]==a||d[3*i+j]+d[9+3*i+j]+d[18+3*i+j]==a){C.innerHTML=\"XnO\"[c+ 1]+\"✌\";e=0;return}}else if(2==f&&34<h||d[h+7])for(l.innerHTML=\"XnO\"[c+1],a=4*(d[h]=c),i=6;i--;)for(j=7;j--;)if(4>j&&d[7*i+j]+d[7*i+j+1]+d[7*i+j+2]+d[7*i+j+3]==a||3>i&&d[7*i+j]+d[7*i+j+7]+d[7*i+j+14]+d[7*i+j+21]==a||3>i&&4>j&&d[7*i+j]+d[7*i+j+8]+d[7*i+j+16]+d[7*i+j+24]==a||3>i&&2<j&&d[7*i+j]+d[7*i+j+6]+d[7*i+j+12]+d[7*i+j+18]==a){C.innerHTML=\"XnO\"[c+1]+\"✌\";e=0;return}c=-c;C.innerHTML=\"XnO\"[c+1];-1==d.indexOf(0)&&(C.innerHTML=\"=\",e=0)}};"
  },
  {
    "path": "TestCases/gitHub#30-webglContext_create_charset.js",
    "content": "gl=canvas.getContext(\"webgl\", { antialias: !0, stencil: !0 })||canvas.getContext(\"experimental-webgl\", { antialias: !0, stencil: !0 });\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/gitHub#31-direct-hyphenBeginsBlock.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,\r\n123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\]^_`abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst"
  },
  {
    "path": "TestCases/gitHub#31-direct-hyphenEndsBlock.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n !'#$%&'()\r\n./0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\]^_`abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst"
  },
  {
    "path": "TestCases/gitHub#31-direct-singleHyphen.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n !'#$%&'()*+,\r\n./012345678:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\]^_`abcdefghijklmnopqrstuvwx\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst"
  },
  {
    "path": "TestCases/gitHub#31-negated-hyphenBeginsBlock.js",
    "content": "-\nabcdabcdabcd\nefghefghefgh\nijklijklijkl\nmnopmnopmnop\nqrstqrstqrst\nuvwxyz"
  },
  {
    "path": "TestCases/gitHub#31-negated-hyphenEndsBlock.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f \r\n-\r\n!\"#$%&'()*+,!\"#$%&'()*+,!\"#$%&'()*+,!\"#$%&'()*+,"
  },
  {
    "path": "TestCases/gitHub#31-negated-singleHyphen.js",
    "content": "-\r\n$%&'$%&'$%&'$%&'$%&'$%&'$%&'$%&'\r\n#()+#()+#()+#()+#()+#()+#()+#()+\r\n\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n012345678901234567890123456789\r\n:!<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVXWYZABCDEFGHIJKLMNOPQRSTUVXWYZ\r\n[\\]^_`\r\nabcdefghijklmnopqrstuvwxyz\r\n{|}~"
  },
  {
    "path": "TestCases/gitHub#44-setInterval_arrowFunctionMultiParam.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((x,y,z)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#44-setInterval_arrowFunctionNoParam.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(()=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#44-setInterval_arrowFunctionSingleParam.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(x=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#47-packer_from92AndReplace.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,-./\r\n456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr"
  },
  {
    "path": "TestCases/gitHub#47-packer_from92NoReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_from92ReplaceFails.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,-./\r\n3456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr"
  },
  {
    "path": "TestCases/gitHub#47-packer_from93AndReplace.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,-./\r\n3456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\bcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nuvwxuvwxuvwx\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr"
  },
  {
    "path": "TestCases/gitHub#47-packer_from93NoReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_rangesBeyond.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,-./\r\n0123456789:;<=>?\r\nAFGHIJKQRSTUVWXYZ\r\n[\\]^_`abcdefghijklmnopqrstuvwxyz`\r\n{|}\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_to92AndReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n3456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVW\r\n]^_`abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_to92NoReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWX\r\n]^_`abcdefghijklmnopqrstuvwxyz`\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_to93AndReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n3456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVW\r\n^_`abcdefghijklmnopqrstuvwxyz`\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_to93NoReplace.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWX\r\n^_`abcdefghijklmnopqrstuvwxyz\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#47-packer_to93ReplaceFails.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n23456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVW\r\n^_`abcdefghijklmnopqrstuvwxyz`\r\n{|}~\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\ncdlzcdlzcdlz\r\nk,grk,grk,gr\r\n"
  },
  {
    "path": "TestCases/gitHub#55-bothQuotesInUse.js",
    "content": "v=0;setInterval(()=>{s=\"'\";t='\"';s='\"'+s;t='\"'+t;u=(v&1?s:t);u+=t+s+t;z='\"'+\"'\"+t+s+t;console.log(u)}, 40);"
  },
  {
    "path": "TestCases/gitHub#55-quotesAsOnlyTokens.js",
    "content": "`\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f`\r\n`! !#$%&()*+,-./`\r\n0123456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\]^_abcdefghijklmnopqrstuvwxyz\r\n`{|}~`\r\nabcdabcdabcd\r\nefghefghefgh\r\nijklijklijkl\r\nmnopmnopmnop\r\nqrstqrstqrst\r\n"
  },
  {
    "path": "TestCases/gitHub#55-sameStringInAllQuotes.js",
    "content": "v=0;setInterval(()=>{s=\"message\";t='message';s=`message`+s;t='message'+t;u=(v&1?s:t);u+=t+s+t;z='message'+\"message\"+`message`+t+s+t;console.log(u)}, 40);"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_arrowComplexValues.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((w=t,x=y=z=0,aa)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_arrowMultipleValues.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((x=2,y=1,z=0)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_arrowNoValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((x,y,z)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_arrowOneValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((x,y,z=0)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_arrowSingleValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval((x=0)=>{if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_standardComplexValues.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(w=t,x=y=z=0,aa){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_standardMultipleValues.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(x=2,y=1,z=0){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_standardNoValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(x,y,z){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_standardOneValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(x,y,z=0){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#56-setInterval_standardSingleValue.js",
    "content": "t=0;a=a.cloneNode(p=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(0,0,150,150);d=e.data;setInterval(function(x=0){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33)"
  },
  {
    "path": "TestCases/gitHub#57-templateLiteralOneVariable.js",
    "content": "a=a.cloneNode($=[s=5]);cc=a.getContext(\"2d\");e=cc.getImageData(T=0,0,150,150);d=e.data;setInterval(function(){if(T%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)$[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],T=-6;++T;)for(i=0;++i-6;)$[256*(y+T&255)+(x+i&255)]+=.01}f=Math.cos(++T/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=$[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+T)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=`hsl(256,25%,${Math.max(0,100-T)}%`;c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(T-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(T-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(T-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,T-1200),Math.max(1,T-1100));n=250-75*Math.cos(Math.max(0,Math.min(T-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(T-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33);"
  },
  {
    "path": "TestCases/gitHub#58-lettersOnly.js",
    "content": "a=b=c=d=e=0;for(k=0;k<50;++k)a+=k;for(k=0;k<50;++k)b+=k*k;for(k=0;k<50;++k)c+=k*k*k;for(p=1;p<50;++p)d+=1/p;for(p=1;p<50;++p)e+=1/p/p;"
  },
  {
    "path": "TestCases/gitHub#58-numberAsMostFrequent.js",
    "content": "a=b=c=d=e=k=0;for(2==e&&k=1;k<50;++k)a+=k;for(2==a&&k=5;k<50;++k)b+=k*k;for(20==b&&k=9;k<50;++k)c+=k*k*k;for(p=1;p<50;++p)d+=1/p;for(p=1;p<50;++p)e+=1/p/p;"
  },
  {
    "path": "TestCases/gitHub#59-negatedRangeMerge-1vs3.js",
    "content": "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\r\n! !'#$%&'()*+,-./\r\n456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n[\\]`_^`\r\nabcdefghijklmnopqrstuvwxyz\r\n{|~\r\nabcdabcdabcd"
  },
  {
    "path": "TestCases/gitHub#63-backtick2DContext.js",
    "content": "a=a.cloneNode(p=[s=5]);cc=a.getContext`2d`;e=cc.getImageData(t=0,0,150,150);d=e.data;setInterval(function(){if(t%1275<1){cc.fillStyle=n=cc.createRadialGradient(225,75,25,225,75,60);n.addColorStop(x=y=f=0,\"rgb(\"+[50,31+s%15&255,15+s%32&255]);n.addColorStop(1,\"#000\");cc.fillRect(150,0,150,150);for(;f<256*256;)p[f++]=Math.min(0,3*Math.cos((f&255)/20)-3*Math.cos(f/12800)-s%7+2);for(n=3e5;--n;s*=(256-s)/65)for(x+=[0,1,0,-1][s&3],y+=[0,1,0,-1][3-s&3],t=-6;++t;)for(i=0;++i-6;)p[256*(y+t&255)+(x+i&255)]+=.01}f=Math.cos(++t/200);for(y=-75;++y<75;)for(x=-(u=Math.sqrt(75*75-y*y)&255),q=4*((y+75)*150+75-u);++x<u;){w=Math.sqrt(75*75-y*y-x*x);i=p[((128*Math.atan2(n=y*Math.sin(f)+Math.cos(f)*w,x)+t)/Math.PI&255)+256*(Math.acos((-w*Math.sin(f)+Math.cos(f)*y)/75)*256/Math.PI&255)];n=.1+.008*Math.max(0,n-x);d[q++]=n*(i<0?128-s%128:s%128+s%25*i)&255;d[q++]=n*(i<0?-7*i:75+s%50*i)&255;d[q++]=n*(i<0?s%128:s%75*i)&255;d[q++]=255}cc.putImageData(e,0,0);c.fillStyle=\"hsl(256,25%,\"+Math.max(0,100-t)+\"%\";c.fillRect(0,0,a.width,a.height);c.save(c.fillStyle=\"#fff\");c.translate(.5*a.width,a.height/2);c.rotate(-.2-.2*Math.cos(Math.max(0,Math.min(t-100,300))*Math.PI/300));c.translate(u=800*Math.cos(Math.max(0,Math.min(t-90,300))*Math.PI/300)-400,500-500*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300));for(i=0;++i-7500;)c.fillRect(i*i%7525-1500,i%2525-1500,Math.max(i%15/7,t-1200),Math.max(1,t-1100));n=250-75*Math.cos(Math.max(0,Math.min(t-350,300))*Math.PI/300)-150*Math.cos(Math.max(0,Math.min(t-1000,300))*Math.PI/300);c.drawImage(a,0,0,150,150,1200-n,-n,2*n,2*n);n=s|40;c.globalCompositeOperation=\"lighter\";for(i=0;++i-12;)c.drawImage(a,150,0,150,150,-n,-25-n,2*n,2*n);for(i=0;++i-75;)n=25*(i&6),c.drawImage(a,150,0,150,150,-u*i-n,25*i-n,2*n,2*n);c.restore()},33);"
  },
  {
    "path": "TestCases/gitHub#63-backtickWebGLContext.js",
    "content": "for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(),2,5120,bD(B=ET-3,Int8Array.of(B,setInterval(x=>dr(6,vertexAttrib1f(1,NO+=.01),3),A=s=>sS(S=cS(FN++),s)|ce(S)|aS(P,S)),!A(`${V=\"varying lowp vec4 C,U;\\n#define R(a)mat2(cos(a),-sin(a),sin(a),cos(a))\\n#define D length(vec2(length(q.xy)-2.,q.z))-.1\\nvoid main(){\"}lowp float t=0.,d,e,f;for(int i=0;i<99;i++){lowp vec3 p=vec3(C.xy*3.,t-3.),q=p;q.yz*=R(U.x-.8);q.xy*=R(U.x-.6);e=max(length(q.xz)-.15,abs(q.y)-2.);q=p;q.xz*=R(.8);q.yz*=R(.6);f=D;q=p;q.xz*=R(-.8);q.yz*=R(.6);f=min(f,D);d=min(e,f);t+=d;if(d<.01){d=1.5-step(length(p),1.)-float(i)/5.;break;}}gl_FragColor=vec4(d,d,d,1);}`),B,!eV(bf(B,cB())),!A(`attribute vec4 A,B;${V}gl_Position=C=A;U=B;}`)),B+82),lo(P),ug(P))"
  },
  {
    "path": "TestCases/gitHub#64-URIError.js",
    "content": "﻿c.fillText('✪🅼🅼🅼', 5, 165);"
  },
  {
    "path": "TestCases/gitHub#65-backslash_invalidEscapeSequence.js",
    "content": "﻿b.innerHTML=`<div style=width:95vw;height:95vh;perspective:500px;overflow:hidden;background:#7e2><div style=transform-style:preserve-3d;margin:50vh+50vw;transform-origin:0+0 id=s><div style=width:55px;font:40px'' id=h>🐰<div style=transform:rotate(180deg)translateY(9px);font:50px''>🎩`;k=[];onkeydown=e=>k[e.which-37]=1;onkeyup=e=>k[e.which-37]=0;x=-400;y=-630;a=1.35;m=[0,10240,45022,59464,27080,59658,114174,0];z=1;for(i in m){for(H=17;H--;){if(m[i]>>H&1){s.innerHTML+=`<div style=width:255px;height:255px;background:#b31;position:fixed;top:${250*i}px;left:${250*H}px>`;}else s.innerHTML+=`<div id=t${i+H} style=font-size:120px;position:fixed>${[...'🌳🌲🌵'][(i*H+z++)%3]}`;}}s.innerHTML+=`<div id=t${616} style=font-size:30px>🥕`;t=0;c=0;setInterval(e=>{t+=.3;v=x;w=y;if(k[2])a-=.05;else if(k[0])a+=.05;else if(k[1]){y+=20*Math.cos(a);x+=20*Math.sin(a)}else if(k[3]){y-=20*Math.cos(a);x-=20*Math.sin(a)}if(!(m[~~(-y/250)]>>(~~(-x/250))&1)){x=v;y=w}h.style.transform=`translateX(${-x-20}px)translateY(${-y-20}px)translateZ(${60-14*Math.sin(t)}px)rotateZ(${-a}rad)rotateX(-90deg)`;if(!c)s.style.transform=`rotateX(55deg)rotateZ(${a}rad)translateX(${x}px)translateY(${y}px)`;else{s.style.transition=`5s`;s.style.transform=`rotateX(30deg)translateX(-2200px)translateY(-2000px)translateZ(-2000px)`;a=0;}for(i in m){for(H=17;H--;){if(self[`t${i+H}`]){if(c)self[`t${i+H}`].style.transition=`5s`;self[`t${i+H}`].style.transform=`translateX(${H*250+99}px)translateY(${i*250-99}px)translateZ(160px)rotateZ(${-a}rad)rotateX(-90deg)scale(2.5)`;}}}if(x<-4100)c=1},33)"
  },
  {
    "path": "TestCases/gitHub#73-backslash_unexpandedToken.js",
    "content": "module.exports=function(n){function e(n,e){for(;n instanceof Array&&n[0]in e&&e[n[0]].M;)n=e[n[0]](...n.slice(1));return n}function t(n,r){for(;;){if(!(n instanceof Array))return i(n,r);if(n=e(n,r),!(n instanceof Array))return i(n,r);if(\"def\"==n[0])return r[n[1]]=t(n[2],r);if(\"~\"==n[0]){let e=t(n[1],r);return e.M=1,e}if(\"`\"==n[0])return n[1];if(\".-\"==n[0]){let e=i(n.slice(1),r),t=e[0][e[1]];return 2 in e?e[0][e[1]]=e[2]:t}if(\".\"==n[0]){let e=i(n.slice(1),r),t=e[0][e[1]];return t.apply(e[0],e.slice(2))}if(\"try\"==n[0])try{return t(n[1],r)}catch(e){return t(n[2][2],i([n[2][1]],r,[e]))}else if(\"fn\"==n[0]){let e=function(...e){return t(n[2],i(n[1],r,e))};return e.A=[n[2],r,n[1]],e}if(\"let\"==n[0]){r=Object.create(r);for(let e in n[1])e%2&&(r[n[1][e-1]]=t(n[1][e],r));n=n[2]}else if(\"do\"==n[0]){let e=i(n.slice(1,n.length-1),r);n=n[n.length-1]}else if(\"if\"==n[0])n=t(n[1],r)?n[2]:n[3];else{let e=i(n,r),t=e[0];if(!t.A)return t(...e.slice(1));n=t.A[0],r=i(t.A[2],t.A[1],e.slice(1))}}}let i=function(e,i,r){return r?(i=Object.create(i),e.some((n,t)=>\"&\"==n?i[e[t+1]]=r.slice(t):(i[n]=r[t],0)),i):e instanceof Array?e.map((...n)=>t(n[0],i)):typeof \"\"==typeof e?e in i?i[e]:n.throw(e+\" not found\"):e};return n=Object.assign(Object.create(n),{js:eval,eval:(...e)=>t(e[0],n),\"=\":(...n)=>n[0]===n[1],\"<\":(...n)=>n[0]<n[1],\"+\":(...n)=>n[0]+n[1],\"-\":(...n)=>n[0]-n[1],\"*\":(...n)=>n[0]*n[1],\"/\":(...n)=>n[0]/n[1],isa:(...n)=>n[0]instanceof n[1],type:(...n)=>typeof n[0],new:(...n)=>new(n[0].bind(...n)),del:(...n)=>delete n[0][n[1]],throw:(...n)=>{throw n[0]},read:(...n)=>JSON.parse(n[0]),slurp:(...n)=>require(\"fs\").readFileSync(n[0],\"utf8\"),load:(...e)=>t(JSON.parse(require(\"fs\").readFileSync(e[0],\"utf8\")),n),rep:(...e)=>JSON.stringify(t(JSON.parse(e[0]),n))})}"
  },
  {
    "path": "TestCases/gitHub#83-backslash_largerOutput.js",
    "content": "for(b.innerHTML=`<div style=transform-style:preserve-3d;position:fixed;perspective:80vh;margin:40vh+50vw><div style=transform-style:preserve-3d;position:fixed;transform:translateX(-30vh)translateZ(-30vh)rotateX(55deg)rotateZ(28deg><div id=s style=transform-style:preserve-3d;position:fixed;width:72vh;height:48vh;background:#19c;animation:a+9s><style>@keyframes a{50%{transform:rotateZ(-50deg`,m=`055000000000000552000500067760005012210500066660005012210555558855555012210000000000000012210000004400000012211111111111113112222211111122220222222211222222100000222211222222100000222211111222000000222211111222000000`,i=12;i--;)for(L=18;L--;)+m[18*i+L]&&(s.innerHTML+=`<div style=transform-style:preserve-3d;position:fixed;transform:translateY(${i*4+(m[i*18+L]==4?-15:0)}vh)translateX(${L*4}vh)translateZ(${[1,1,1,2,1,1,1,1,13][m[i*18+L]]}vh)scaleX(1.1)scaleY(${m[i*18+L]==4?5:1.1})scaleZ(${[,.5,.8,.2,.2,5,8,11,2][m[i*18+L]]}><p style=transform-style:preserve-3d;position:fixed;width:4vh;height:4vh;background:#${[`349`,`ed7`,`0a0`,`da4`][m[i*18+L]]||`ddd`};transform:translateZ(4vh><p style=transform-style:preserve-3d;position:fixed;width:4vh;height:4vh;background:#${[,`ed7`,`0a0`,`da4`][m[i*18+L]]||`ddd`};transform:rotateY(90deg)translate(-2vh)translateZ(-2vh><p style=transform-style:preserve-3d;position:fixed;width:4vh;height:4vh;background:#${[,`ed7`,`0a0`,`da4`][m[i*18+L]]||`ddd`};transform:rotateY(90deg)translate(-2vh)translateZ(2vh><p style=transform-style:preserve-3d;position:fixed;width:4vh;height:4vh;background:#${[,`fe8`,`0c0`,`da4`][m[i*18+L]]||`edd`};transform:rotateX(90deg)translateY(2vh)translateZ(-2vh>`);for(i=7;i--;)s.innerHTML+=`<div style=transform-style:preserve-3d;position:fixed;transform:translateX(${[10,26,31,2,59,54,10][i]}vh)translateY(${[-11,-6,2,-5,-5,14,14][i]}vh)translateZ(${[20.9,33,45,21,21,21,21][i]}vh)scaleX(${[2.5,1,.5,.5,.5,.3,.3][i]})scaleY(${[1.5,1,.5,.5,.5,.3,.3][i]})scaleZ(.5><p style=transform-style:preserve-3d;position:fixed;width:20vmin;height:20vmin;background:#b31;clip-path:polygon(50%+0,0+99%,99%+99%);transform:translateZ(9vmin)translateY(4.9vmin)rotateX(-60deg><p style=transform-style:preserve-3d;width:20vmin;height:20vmin;position:fixed;background:#b31;clip-path:polygon(50%+0,0+99%,99%+99%);transform:translateZ(9vh)translateX(-4.9vh)rotateY(-60deg)rotateZ(90deg><p style=transform-style:preserve-3d;width:20vmin;height:20vmin;position:fixed;background:#a31;clip-path:polygon(50%+0,0+99%,99%+99%);transform:translateZ(9vh)translateX(4.9vh)rotateY(60deg)rotateZ(-90deg>`"
  },
  {
    "path": "TestCases/gitHub#83-packer_9Alone.js",
    "content": "'\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f'\r\n'! !#$%&'()*+,-./\r\n3456789:;<=>?@\r\nABCDEFGHIJKLMNOPQRSTUVW\r\n[\\]^_`abcdefghijklmnopqrsuvwxyz`\r\n{|}~\r\nabcdabcdabcd;\r\nefghefghefgh;\r\nijklijklijkl;\r\nmnopmnopmnop;\r\nqrstqrstqrst;\r\ncdlzcdlzcdlz;\r\nk,grk,grk,gr;\r\n"
  },
  {
    "path": "TestCases/gitHub#85-backslashSequence.js",
    "content": "var s=\"\\\\\";\ns=s+s;\nvar z=s;\nvar s=\"abcdefghijklmnopqrstuvwxyz\";\nvar k=s+z;\nvar w=k+\"aab\\\\xyz\";\nvar d=[];\nfor (var i=0; i<10; ++i) { d.push(i); d.push(i+\"\\\\\"); }"
  },
  {
    "path": "TestCases/gitHub#85-flappyDragon.js",
    "content": "B=E=C=0;s=a.height/1E3;R=[];for(i=0;1E3>i;i++)R[i]=R[i+1E3]=8*Math.random()|0;k=G=6;r=50;x=200;y=500;V=0;S=20;ontouchstart=onmousedown=function(){E?location=location:(B=1,V=45)};(F=function(){c[f=\"fillStyle\"]=\"#E50\";c.fillRect(0,0,2E3,2E3);c.save();c.scale(s,s);c[f]=\"#920\";c[b=\"beginPath\"]();c[M=\"moveTo\"](-S,0);for(i=2E3;i--;)i%20?c[L=\"lineTo\"](40*i-S,20+4*R[i]):(c[L](40*i-S,100*R[i]),c[L](40*i-S-4,100*R[i]));c[L](40*i-S,0);c.fill();c[b]();c[M](-S,2E3);for(i=2E3;i--;)i%20?c[L=\"lineTo\"](40*i-S,980-4*R[i]):(c[L](40*i-S,100*(R[i]+3)),c[L](40*i-S-4,100*(R[i]+3)));c[L](40*i-S,1E3);c.fill();c[f]=\"#000\";c[b]();z=\"\u0017\u0010fE\bFf&\u0019\u0005\u0005\u0007\u000b{{~_=,\u0019;=vviJ\u0019\u0019.jfVx/.OoyxzyhkhEwf\u001774)\\\\n\u0017\u0007$fwwuvtU`\"+(10<V?\"iZ[*)yj:*xm**y|Ktdww\u000654\u0014\u0015#5\u0004\u0015Dy\\\\iz[Kzi[Jiijk[e@1!\u0003\u0003\u0005\b\b\u0007\":\"zl]LfU{\\\\lKtBUh{zzU66xxgxg5\u0005\u0016\u0006\u0004\u0016\u0019\\\\n\b\b\u0005\u0006&xxyz{vfwwxyDfwxxE\u0003\u0004\u0004\u0005\u0007\u0006\b\u0007\u0019l^,\u001c \b\");c[M](v=x,w=y-=V);for(i in z)c[L](v+=16-2*(z.charCodeAt(i)&15),w+=8-2*(z.charCodeAt(i)>>4));c.fill();B&&(V-=G);B&&!E&&(S+=20,4E4<S&&(S-=4E4));B&&k++;!B||E||k%40||(C++,15<r&&r--);if(50>y||950<y||!((S+180)%800)&&(y<100*R[20*~~(S/800)+20]+50||y>100*R[20*~~(S/800)+20]+250))E=1;c[f]=\"#fff\";c.font=\"9em a\";c.fillText(C,40,200);c.restore();setTimeout(F,r)})()"
  },
  {
    "path": "TestCases/gitHub#9-hashloop.js",
    "content": "(u=function(){for(requestAnimationFrame(u),a.width=x=1023,a.height=h=x*innerHeight/innerWidth|1,H=performance.now()/15|1,c.textAlign=\"center\",c.font=\"2em cursive\",q=170,c.fillStyle=\"hsl(\"+q+\",20%,55%)\",c.fillRect(0,0,1023,h),w=555;x--;)if(s=x%3?1&H++||-1:0,c[1023&H]=x&&c[1023&H]||8+Math.random()*h/2,Y=48*Math.sin(H/511-3*Math.sin(H/511*3))+s*c[1023&H]+h/2,X=1023-x+(511-x)*Y/h,c.globalAlpha=1,q=170,s)if(q+=31&Y,c.fillStyle=\"hsl(\"+q+\",55%,200%)\",H%511){if(1&Y&&(c.fillStyle=\"hsl(\"+q+\",55%,20%)\"),c.fillRect(X+3*Math.sin(q++),Y+Math.sin(q++),1,5),c.fillRect(X+3*Math.sin(q++),Y+Math.sin(q++),1,5),c.fillRect(X+3*Math.sin(q++),Y+Math.sin(q++),1,5),c.fillRect(X+48*Math.sin(q++),Y+3*Math.sin(q++),1,1),c.fillRect(X+48*Math.sin(q++),Y+3*Math.sin(q++),1,1),c.fillRect(X+48*Math.sin(q++),Y+3*Math.sin(q++),1,1),!(H%(9+3*Math.sin(H/511/3)|1)))for(z=15&Y,z+=6,Y-=6*z,c.globalAlpha=.3;z--;)c.beginPath(),c.lineTo(X,Y),c.lineTo(X-2*z+3*Math.sin(q++),Y+6*z+3*Math.sin(q++)),c.lineTo(X+2*z+3*Math.sin(q++),Y+6*z+3*Math.sin(q++)),c.fill()}else c.fillText([\"Cross browser\",\"1kb demos\",\"February 2015\",\"JS1k 2015\"][H/511&3],X,Y);else if(c.fillStyle=\"hsl(\"+q+\",55%,2%)\",c.fillRect(X,Y,1,5),c.fillRect(X,Y,5,1),Y+=2,c.fillRect(X,Y,5,1),x==w)if(X-=10,c.fillRect(X,Y,20,-10),511>x)for(c.fillRect(X,Y,10,-15),X+=15,c.fillRect(X,Y,2,-15),c.fillStyle=\"hsl(\"+q+\",55%,200%)\",c.globalAlpha=.3,q=H,z=9;z--;)c.fillRect(X+3*Math.sin(q++),Y-6*z+3*Math.sin(q++),z,z);else w-=15;c.globalCompositeOperation=\"destination-in\",H=31*Math.sin(H/511/2+10)+31,H=3>H?9:H*H,c.translate(511,h/2),c.rotate(H/511+.1),c.scale(H,H),X=Y=-3,c.beginPath(),c.moveTo(X,Y),c.arc(X-2,Y+3,2,0,Math.PI/2,0),X+=2,Y+=4,c.moveTo(X,Y),c.arc(X,Y-1,1,Math.PI/2,Math.PI/2*3,1),c.arc(X,Y-3,1,Math.PI/2,Math.PI/2*3,0),X+=3,c.moveTo(X,Y),c.arc(X-2,Y-5,2,0,Math.PI/2,0),X+=2,c.moveTo(X,Y),c.lineTo(X,Y-6),X+=3,c.moveTo(X,Y),c.arc(X-1,Y-2,1,Math.PI/2*3/2,Math.PI/2*3,0),c.stroke()})();"
  },
  {
    "path": "TestCases/hash_using_length.js",
    "content": "t=w=0;c.lineCap=\"round\";setInterval(function(){for(i=720;i;)c.strokeStyle=\"hsl(20,0%,\"+((Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i))/2+\"%)\",c.lineWidth=(Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i),c.beginPath(),c.moveTo((Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i),(Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i)),c.lineTo((Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i),(Math.cos(t/20.372)+1)*x.charCodeAt(--i)+(-Math.cos(t/20.372)+1)*x.charCodeAt(--i)),c.stroke();w=w?--w:++t&63?w:64},40)"
  },
  {
    "path": "TestCases/webglContext_create1.js",
    "content": "gl=canvas.getContext(\"experimental-webgl\");\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_create2.js",
    "content": "gl=canvas.getContext(\"webgl\");\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_create3.js",
    "content": "gl=canvas.getContext(\"experimental-webgl\")||canvas.getContext(\"webgl\");\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_create4.js",
    "content": "gl=canvas.getContext(\"webgl\")||canvas.getContext(\"experimental-webgl\");\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_create5.js",
    "content": "o={ antialias: true, stencil: true };\ngl=canvas.getContext(\"webgl\", o)||canvas.getContext(\"experimental-webgl\", o);\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_create6.js",
    "content": "gl=canvas.getContext(\"webgl\", { antialias: true, stencil: true })||canvas.getContext(\"experimental-webgl\", { antialias: true, stencil: true });\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\n\n"
  },
  {
    "path": "TestCases/webglContext_substringHash.js",
    "content": "gl=canvas.getContext(\"experimental-webgl\");\ngl.clearColor(0.0, 0.0, 0.0, 1.0); \ngl.clearDepth(1.0);                \ngl.enable(gl.DEPTH_TEST);           \ngl.depthFunc(gl.LEQUAL); \ngl.bindBuffer(gl.ARRAY_BUFFER, b = gl.createBuffer());\ngl.enableVertexAttribArray();\n\n"
  },
  {
    "path": "bin/regpack",
    "content": "#! /usr/bin/env node\n\nconst cmdRegPack = require('..').cmdRegPack;\nvar argv = require('minimist')(process.argv.slice(2));\nvar input = argv._[0];\n\nif (input != '-') {\n    result = cmdRegPack(require('fs').readFileSync(input, 'utf-8'), argv);\n    console.log(result);\n} else {\n    process.stdin.resume();\n    process.stdin.setEncoding('utf8');\n    var buffer = '';\n    process.stdin.on('data', function(data) {\n        buffer += data;\n    });\n    process.stdin.on('end', function() {\n      result = cmdRegPack(buffer, argv);\n      console.log(result);\n    });\n}\n"
  },
  {
    "path": "changelog.txt",
    "content": "v5.0.4 - July 2022\n\nFixed bugs\n- #59 : Negated char class : optimal range merge\n- #74 : Line break removed, leaving no separator between instructions\n- #79 : Discard C-style and xml-style comments\n- #86 : Shorten context properties using available variable names\n- #96 : Do not remove newlines or trailing blanks in template literals\n- #97 : setTransform turns into getTransform on Chrome\n\n------------------------------\nv5.0.3 - February 2019\n\nFixed bugs\n- #87 : Crusher always ignores first character when searching for patterns\n- #88 : Unable to allocate a variable name for setInterval()\n- #89 : Empty mapping crashes thermal viewer\n- #94 : Cannot read property 'first' of undefined\n\n------------------------------\nv5.0.2 - October 2018\n\nFixed bugs\n- #76 : Reassign variable names : still considering (but not replacing) text in strings\n- #82 : Nested backticks in template literal causes string renaming module to miss occurrences\n- #83 : Don't use \"\\\" as a token if avoiding it makes the output smaller\n- #85 : Suboptimal packing due to incorrect assumed length of escape sequence\n\n------------------------------\nv5.0.1 - February 2017\n\nFixed bugs\n- #65 : RegPacked code contains invalid escape sequences\n- #70 : Line endings in bin/regpack\n- #72 : Optimization for refactor setInterval() when no initialization code\n- #73 : Unpacked source has \"in\" strings in wrong places\n- #75 : Variables leak into global space, should be declared local\n\n------------------------------\nv5.0.0 - February 2017\n\nNew features :\n- #26 : Add a heatmap vizualisation\n- #33 : [Online demo] Sync the input textarea with the URL's hash\n- #44 : Support for arrow function when refactoring setInterval\n- #47 : Support escaped characters in character class\n- #48 : Crusher phase - list patterns that are \"almost\" gains\n- #54 : Opt-out flag for ES6 features\n- #55 : Harmonize strings delimiters inside the code, to free \" or ' as compression token\n- #56 : Support for default parameters when refactoring setInterval()\n- #63 : ES6 : Support syntax canvas.getContext`2d` in module \"hash context\"\n\nFixed bugs\n- #50 : Crusher splits Unicode high/low surrogate pairs, producing incorrect strings \n- #52 : regPack.html - Base64 output fails for chars outside of the Latin1 range\n- #57 : $ in template literals\n- #58 : Hash context module uses digit as loop variable\n\n------------------------------\nv4.0.1 - February 2016\n\nFixed bugs\n - #39 : Rename variable in unpacking loop and protect variable d and g by default \n - #40 : Fix use in Node and Better CLI support\n - #41 : Visualization misses patterns that end at the last character\n - #45 : Closing bracket ] not allowed in character class range definition\n\n------------------------------\nv4.0 - December 2015\n\nNew features :\n - #16 : Add an option for base64 encoded output\n - #19 : Use setInterval() to evaluate the unpacked code\n - #21 : Hash not only canvas functions, but also canvas properties\n - #23 : Extra hashing functions\n - #25 : Visualize the compressed patterns inside the source code\n\nFixed bugs\n - #10 : Use varsNotReassigned in renameObjectMethods \n - #17 : Hashing code confuses first and second 2D context\n - #20 : Code packed in FF fails in Chrome because of deprecated context methods \n - #27 : Do not let variable renaming interfere with the use of the variable _\n - #28 : Unable to create 2D/GL/Audio context under Node.js\n - #29 : Variable renaming gives different results under FF / Chrome / Node.js\n - #30 : support for '!' character in WebGL context declaration\n - #31 : Single - character misinterpreted as range in RegExp\n\n------------------------------\nv3.0.2 - February 2015\n\nFixed bugs\n - #9 : Canvas hashing can overwrite the \"protected\" variables\n - #11 : Different results between FF and other browsers, method array.fill() interferes with algorithm\n - #12 : Disable AudioContext hashing option if the browser does not support the WebAudio API\n\n------------------------------\nv3.0.1 - March 2014\n\nFixed bugs\n - #2 : support for Unicode characters in regular expression\n - #4 : original size showed without added escapes\n - #5 : correct size in bytes after preprocessing stage\n------------------------------\nv3.0 - February 2014\n\nAdded preprocessing stage at the beginning of compression workflow.\nPreprocessing :\n - method hashing/renaming for 2D context, WebGL context and AudioContext \n - variable renaming to free tokens for the crusher\n \nFixed bugs :\n - Incorrect CR/LF handling for code using characters below 10\n - Character 127 ignored in negated char class regexp\n------------------------------\nv2 - April 2013\n\nUse of regular expressions with negated char class (listing characters not to match on).\n------------------------------\n"
  },
  {
    "path": "contextDescriptor_browser.js",
    "content": "/**\n * @constructor\n * The ContextDescriptor class exposes the contents of the different contexts (2D graphic, GL graphics and Audio)\n * in a structure tailored for the use of the preprocessor.\n * @see Shapeshifter.js\n * It provides three descriptions :\n *   - canvas2DContextDescription : for a 2D context of a canvas\n *   - canvasGLContextDescription : for a webgl context of a canvas\n *   - audioContextDescription : for an AudioContext (if supported by the current browser)\n *\n * This implementation is tailored for in-browser execution (as opposed to server-side execution with Node.js)\n * It derives the contents from actual instances of the contexts.\n */\n function ContextDescriptor()\n {\n\tvar canvas = document.createElement(\"canvas\");\n\tvar context2D = canvas.getContext(\"2d\");\n\tthis.canvas2DContextDescription = this.describeContext(context2D);\n\t\n\tcanvas = document.createElement(\"canvas\");\n\tvar contextGL = canvas.getContext(\"webgl\");\n\tthis.canvasGLContextDescription = this.describeContext(contextGL);\n\t\n\tvar audioContext = [];\t// have an empty description if the AudioContext is not supported\n\tif (typeof AudioContext !== \"undefined\") {\n\t\taudioContext = new AudioContext;\n\t}\n\tthis.audioContextDescription = this.describeContext(audioContext);\n\tthis.balanceContexts(); // all contexts since Firefox 102 / Chromium 103\n }\n \n ContextDescriptor.prototype = {\n \n\tdescribeContext : function(context) {\n\t\tvar description = { properties : [], constants : {} };\n\t\tfor (var prop in context) {\n\t\t\tif (prop.match(/^[A-Z_0-9]*$/)) {\t// constant : name in capitals only\n\t\t\t\tdescription.constants[prop] = context[prop];\n\t\t\t}\n\t\t\tdescription.properties.push(prop);\n\t\t}\n\t\treturn description;\n\t},\n\t\n\t/**\n\t * Fix for issue #20 - make sure the behavior is identical in all browsers\n\t * by adding extra methods / properties to the description of 2D context\n\t * to even out the property list for all browsers.\n\t * \n\t * Update for #97 in July 2022 : GL and Audio contexts also need to be levelled\n\t * \n\t * Needs to be reconsidered at each new browser revision \n\t * (current reference are for FF 102 / Chromium 103)\n\t */\n\tbalanceContexts : function() {\n\t\tthis.canvas2DContextDescription.properties.push(\"fontKerning\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"fontStretch\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"fontVariantCaps\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"getContextAttributes\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"imageSmoothingQuality\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"isContextLost\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"letterSpacing\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"mozCurrentTransform\"); // FF-prefixed\n\t\tthis.canvas2DContextDescription.properties.push(\"mozCurrentTransformInverse\"); // FF-prefixed\n\t\tthis.canvas2DContextDescription.properties.push(\"mozImageSmoothingEnabled\"); // FF-prefixed\n\t\tthis.canvas2DContextDescription.properties.push(\"mozTextStyle\"); // FF-prefixed\n\t\tthis.canvas2DContextDescription.properties.push(\"reset\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"roundRect\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"textRendering\");  // Chrome only\n\t\tthis.canvas2DContextDescription.properties.push(\"wordSpacing\");  // Chrome only\n\n\t\tthis.canvasGLContextDescription.properties.push(\"makeXRCompatible\");  // Chrome only\n\t\t\n\t\tthis.audioContextDescription.properties.push(\"createMediaStreamTrackSource\"); // Firefox only\n\t},\n\t\n\n }\n"
  },
  {
    "path": "contextDescriptor_node.js",
    "content": "/**\n * @constructor\n * The ContextDescriptor class exposes the contents of the different contexts (2D graphic, GL graphics and Audio)\n * in a structure tailored for the use of the preprocessor.\n * @see Shapeshifter.js\n * It provides three descriptions :\n *   - canvas2DContextDescription : for a 2D context of a canvas\n *   - canvasGLContextDescription : for a webgl context of a canvas\n *   - audioContextDescription : for an AudioContext (if supported by the current browser)\n *\n * This implementation is a workaround for Node.js environments, which do not provide built-in canvas (nor associated contexts)\n * The values are explicitely set based on FF 50 and Chrome 54\n */\n function ContextDescriptor()\n {\n\t\n\tthis.canvas2DContextDescription = {\n\t\tproperties : [\n\t\t\t\"arc\",\n\t\t\t\"arcTo\",\n\t\t\t\"beginPath\",\n\t\t\t\"bezierCurveTo\",\n\t\t\t\"canvas\",\n\t\t\t\"clearRect\",\n\t\t\t\"clip\",\n\t\t\t\"closePath\",\n\t\t\t\"createConicGradient\",\n\t\t\t\"createImageData\",\n\t\t\t\"createLinearGradient\",\n\t\t\t\"createPattern\",\n\t\t\t\"createRadialGradient\",\n\t\t\t\"direction\",\n\t\t\t\"drawFocusIfNeeded\",\n\t\t\t\"drawImage\",\n\t\t\t\"ellipse\",\n\t\t\t\"fill\",\n\t\t\t\"fillRect\",\n\t\t\t\"fillStyle\",\n\t\t\t\"fillText\",\n\t\t\t\"filter\",\n\t\t\t\"font\",\n\t\t\t\"fontKerning\",\n\t\t\t\"fontStretch\",\n\t\t\t\"fontVariantCaps\",\n\t\t\t\"getContextAttributes\",\n\t\t\t\"getImageData\",\n\t\t\t\"getLineDash\",\n\t\t\t\"getTransform\",\n\t\t\t\"globalAlpha\",\n\t\t\t\"globalCompositeOperation\",\n\t\t\t\"imageSmoothingEnabled\",\n\t\t\t\"imageSmoothingQuality\",\n\t\t\t\"isContextLost\",\n\t\t\t\"isPointInPath\",\n\t\t\t\"isPointInStroke\",\n\t\t\t\"letterSpacing\",\n\t\t\t\"lineCap\",\n\t\t\t\"lineDashOffset\",\n\t\t\t\"lineJoin\",\n\t\t\t\"lineTo\",\n\t\t\t\"lineWidth\",\n\t\t\t\"measureText\",\n\t\t\t\"miterLimit\",\n\t\t\t\"moveTo\",\n\t\t\t\"putImageData\",\n\t\t\t\"quadraticCurveTo\",\n\t\t\t\"rect\",\n\t\t\t\"reset\",\n\t\t\t\"resetTransform\",\n\t\t\t\"restore\",\n\t\t\t\"rotate\",\n\t\t\t\"roundRect\",\n\t\t\t\"save\",\n\t\t\t\"scale\",\n\t\t\t\"setLineDash\",\n\t\t\t\"setTransform\",\n\t\t\t\"shadowBlur\",\n\t\t\t\"shadowColor\",\n\t\t\t\"shadowOffsetX\",\n\t\t\t\"shadowOffsetY\",\n\t\t\t\"stroke\",\n\t\t\t\"strokeRect\",\n\t\t\t\"strokeStyle\",\n\t\t\t\"strokeText\",\n\t\t\t\"textAlign\",\n\t\t\t\"textBaseline\",\n\t\t\t\"textRendering\",\n\t\t\t\"transform\",\n\t\t\t\"translate\",\n\t\t\t\"wordSpacing\",\n\t\t\t// Fix for issue #20 : add - make sure the behavior is identical in all browsers\n\t\t\t// by adding extra methods / properties to the description of 2D context\n\t\t\t// to even out the property list for all browsers.\n\t\t\t\"mozCurrentTransform\", // FF-prefixed\n\t\t\t\"mozCurrentTransformInverse\", // FF-prefixed\n\t\t\t\"mozImageSmoothingEnabled\", // FF-prefixed\n\t\t\t\"mozTextStyle\" // FF-prefixed\n\t\t\t//#32 : removed drawImageFromRect (deprecated, supported up to Chrome 40)\n\t\t], \n\t\tconstants : {}\n\t};\n\t\n\tthis.canvasGLContextDescription = {\n\t\tproperties : [\n\t\t\t\"ACTIVE_ATTRIBUTES\",\n\t\t\t\"ACTIVE_TEXTURE\",\n\t\t\t\"ACTIVE_UNIFORMS\",\n\t\t\t\"ALIASED_LINE_WIDTH_RANGE\",\n\t\t\t\"ALIASED_POINT_SIZE_RANGE\",\n\t\t\t\"ALPHA\",\n\t\t\t\"ALPHA_BITS\",\n\t\t\t\"ALWAYS\",\n\t\t\t\"ARRAY_BUFFER\",\n\t\t\t\"ARRAY_BUFFER_BINDING\",\n\t\t\t\"ATTACHED_SHADERS\",\n\t\t\t\"BACK\",\n\t\t\t\"BLEND\",\n\t\t\t\"BLEND_COLOR\",\n\t\t\t\"BLEND_DST_ALPHA\",\n\t\t\t\"BLEND_DST_RGB\",\n\t\t\t\"BLEND_EQUATION\",\n\t\t\t\"BLEND_EQUATION_ALPHA\",\n\t\t\t\"BLEND_EQUATION_RGB\",\n\t\t\t\"BLEND_SRC_ALPHA\",\n\t\t\t\"BLEND_SRC_RGB\",\n\t\t\t\"BLUE_BITS\",\n\t\t\t\"BOOL\",\n\t\t\t\"BOOL_VEC2\",\n\t\t\t\"BOOL_VEC3\",\n\t\t\t\"BOOL_VEC4\",\n\t\t\t\"BROWSER_DEFAULT_WEBGL\",\n\t\t\t\"BUFFER_SIZE\",\n\t\t\t\"BUFFER_USAGE\",\n\t\t\t\"BYTE\",\n\t\t\t\"CCW\",\n\t\t\t\"CLAMP_TO_EDGE\",\n\t\t\t\"COLOR_ATTACHMENT0\",\n\t\t\t\"COLOR_BUFFER_BIT\",\n\t\t\t\"COLOR_CLEAR_VALUE\",\n\t\t\t\"COLOR_WRITEMASK\",\n\t\t\t\"COMPILE_STATUS\",\n\t\t\t\"COMPRESSED_TEXTURE_FORMATS\",\n\t\t\t\"CONSTANT_ALPHA\",\n\t\t\t\"CONSTANT_COLOR\",\n\t\t\t\"CONTEXT_LOST_WEBGL\",\n\t\t\t\"CULL_FACE\",\n\t\t\t\"CULL_FACE_MODE\",\n\t\t\t\"CURRENT_PROGRAM\",\n\t\t\t\"CURRENT_VERTEX_ATTRIB\",\n\t\t\t\"CW\",\n\t\t\t\"DECR\",\n\t\t\t\"DECR_WRAP\",\n\t\t\t\"DELETE_STATUS\",\n\t\t\t\"DEPTH_ATTACHMENT\",\n\t\t\t\"DEPTH_BITS\",\n\t\t\t\"DEPTH_BUFFER_BIT\",\n\t\t\t\"DEPTH_CLEAR_VALUE\",\n\t\t\t\"DEPTH_COMPONENT\",\n\t\t\t\"DEPTH_COMPONENT16\",\n\t\t\t\"DEPTH_FUNC\",\n\t\t\t\"DEPTH_RANGE\",\n\t\t\t\"DEPTH_STENCIL\",\n\t\t\t\"DEPTH_STENCIL_ATTACHMENT\",\n\t\t\t\"DEPTH_TEST\",\n\t\t\t\"DEPTH_WRITEMASK\",\n\t\t\t\"DITHER\",\n\t\t\t\"DONT_CARE\",\n\t\t\t\"DST_ALPHA\",\n\t\t\t\"DST_COLOR\",\n\t\t\t\"DYNAMIC_DRAW\",\n\t\t\t\"ELEMENT_ARRAY_BUFFER\",\n\t\t\t\"ELEMENT_ARRAY_BUFFER_BINDING\",\n\t\t\t\"EQUAL\",\n\t\t\t\"FASTEST\",\n\t\t\t\"FLOAT\",\n\t\t\t\"FLOAT_MAT2\",\n\t\t\t\"FLOAT_MAT3\",\n\t\t\t\"FLOAT_MAT4\",\n\t\t\t\"FLOAT_VEC2\",\n\t\t\t\"FLOAT_VEC3\",\n\t\t\t\"FLOAT_VEC4\",\n\t\t\t\"FRAGMENT_SHADER\",\n\t\t\t\"FRAMEBUFFER\",\n\t\t\t\"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME\",\n\t\t\t\"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE\",\n\t\t\t\"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE\",\n\t\t\t\"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL\",\n\t\t\t\"FRAMEBUFFER_BINDING\",\n\t\t\t\"FRAMEBUFFER_COMPLETE\",\n\t\t\t\"FRAMEBUFFER_INCOMPLETE_ATTACHMENT\",\n\t\t\t\"FRAMEBUFFER_INCOMPLETE_DIMENSIONS\",\n\t\t\t\"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\",\n\t\t\t\"FRAMEBUFFER_UNSUPPORTED\",\n\t\t\t\"FRONT\",\n\t\t\t\"FRONT_AND_BACK\",\n\t\t\t\"FRONT_FACE\",\n\t\t\t\"FUNC_ADD\",\n\t\t\t\"FUNC_REVERSE_SUBTRACT\",\n\t\t\t\"FUNC_SUBTRACT\",\n\t\t\t\"GENERATE_MIPMAP_HINT\",\n\t\t\t\"GEQUAL\",\n\t\t\t\"GREATER\",\n\t\t\t\"GREEN_BITS\",\n\t\t\t\"HIGH_FLOAT\",\n\t\t\t\"HIGH_INT\",\n\t\t\t\"IMPLEMENTATION_COLOR_READ_FORMAT\",\n\t\t\t\"IMPLEMENTATION_COLOR_READ_TYPE\",\n\t\t\t\"INCR\",\n\t\t\t\"INCR_WRAP\",\n\t\t\t\"INT\",\n\t\t\t\"INT_VEC2\",\n\t\t\t\"INT_VEC3\",\n\t\t\t\"INT_VEC4\",\n\t\t\t\"INVALID_ENUM\",\n\t\t\t\"INVALID_FRAMEBUFFER_OPERATION\",\n\t\t\t\"INVALID_OPERATION\",\n\t\t\t\"INVALID_VALUE\",\n\t\t\t\"INVERT\",\n\t\t\t\"KEEP\",\n\t\t\t\"LEQUAL\",\n\t\t\t\"LESS\",\n\t\t\t\"LINEAR\",\n\t\t\t\"LINEAR_MIPMAP_LINEAR\",\n\t\t\t\"LINEAR_MIPMAP_NEAREST\",\n\t\t\t\"LINES\",\n\t\t\t\"LINE_LOOP\",\n\t\t\t\"LINE_STRIP\",\n\t\t\t\"LINE_WIDTH\",\n\t\t\t\"LINK_STATUS\",\n\t\t\t\"LOW_FLOAT\",\n\t\t\t\"LOW_INT\",\n\t\t\t\"LUMINANCE\",\n\t\t\t\"LUMINANCE_ALPHA\",\n\t\t\t\"MAX_COMBINED_TEXTURE_IMAGE_UNITS\",\n\t\t\t\"MAX_CUBE_MAP_TEXTURE_SIZE\",\n\t\t\t\"MAX_FRAGMENT_UNIFORM_VECTORS\",\n\t\t\t\"MAX_RENDERBUFFER_SIZE\",\n\t\t\t\"MAX_TEXTURE_IMAGE_UNITS\",\n\t\t\t\"MAX_TEXTURE_SIZE\",\n\t\t\t\"MAX_VARYING_VECTORS\",\n\t\t\t\"MAX_VERTEX_ATTRIBS\",\n\t\t\t\"MAX_VERTEX_TEXTURE_IMAGE_UNITS\",\n\t\t\t\"MAX_VERTEX_UNIFORM_VECTORS\",\n\t\t\t\"MAX_VIEWPORT_DIMS\",\n\t\t\t\"MEDIUM_FLOAT\",\n\t\t\t\"MEDIUM_INT\",\n\t\t\t\"MIRRORED_REPEAT\",\n\t\t\t\"NEAREST\",\n\t\t\t\"NEAREST_MIPMAP_LINEAR\",\n\t\t\t\"NEAREST_MIPMAP_NEAREST\",\n\t\t\t\"NEVER\",\n\t\t\t\"NICEST\",\n\t\t\t\"NONE\",\n\t\t\t\"NOTEQUAL\",\n\t\t\t\"NO_ERROR\",\n\t\t\t\"ONE\",\n\t\t\t\"ONE_MINUS_CONSTANT_ALPHA\",\n\t\t\t\"ONE_MINUS_CONSTANT_COLOR\",\n\t\t\t\"ONE_MINUS_DST_ALPHA\",\n\t\t\t\"ONE_MINUS_DST_COLOR\",\n\t\t\t\"ONE_MINUS_SRC_ALPHA\",\n\t\t\t\"ONE_MINUS_SRC_COLOR\",\n\t\t\t\"OUT_OF_MEMORY\",\n\t\t\t\"PACK_ALIGNMENT\",\n\t\t\t\"POINTS\",\n\t\t\t\"POLYGON_OFFSET_FACTOR\",\n\t\t\t\"POLYGON_OFFSET_FILL\",\n\t\t\t\"POLYGON_OFFSET_UNITS\",\n\t\t\t\"RED_BITS\",\n\t\t\t\"RENDERBUFFER\",\n\t\t\t\"RENDERBUFFER_ALPHA_SIZE\",\n\t\t\t\"RENDERBUFFER_BINDING\",\n\t\t\t\"RENDERBUFFER_BLUE_SIZE\",\n\t\t\t\"RENDERBUFFER_DEPTH_SIZE\",\n\t\t\t\"RENDERBUFFER_GREEN_SIZE\",\n\t\t\t\"RENDERBUFFER_HEIGHT\",\n\t\t\t\"RENDERBUFFER_INTERNAL_FORMAT\",\n\t\t\t\"RENDERBUFFER_RED_SIZE\",\n\t\t\t\"RENDERBUFFER_STENCIL_SIZE\",\n\t\t\t\"RENDERBUFFER_WIDTH\",\n\t\t\t\"RENDERER\",\n\t\t\t\"REPEAT\",\n\t\t\t\"REPLACE\",\n\t\t\t\"RGB\",\n\t\t\t\"RGB565\",\n\t\t\t\"RGB5_A1\",\n\t\t\t\"RGBA\",\n\t\t\t\"RGBA4\",\n\t\t\t\"SAMPLER_2D\",\n\t\t\t\"SAMPLER_CUBE\",\n\t\t\t\"SAMPLES\",\n\t\t\t\"SAMPLE_ALPHA_TO_COVERAGE\",\n\t\t\t\"SAMPLE_BUFFERS\",\n\t\t\t\"SAMPLE_COVERAGE\",\n\t\t\t\"SAMPLE_COVERAGE_INVERT\",\n\t\t\t\"SAMPLE_COVERAGE_VALUE\",\n\t\t\t\"SCISSOR_BOX\",\n\t\t\t\"SCISSOR_TEST\",\n\t\t\t\"SHADER_TYPE\",\n\t\t\t\"SHADING_LANGUAGE_VERSION\",\n\t\t\t\"SHORT\",\n\t\t\t\"SRC_ALPHA\",\n\t\t\t\"SRC_ALPHA_SATURATE\",\n\t\t\t\"SRC_COLOR\",\n\t\t\t\"STATIC_DRAW\",\n\t\t\t\"STENCIL_ATTACHMENT\",\n\t\t\t\"STENCIL_BACK_FAIL\",\n\t\t\t\"STENCIL_BACK_FUNC\",\n\t\t\t\"STENCIL_BACK_PASS_DEPTH_FAIL\",\n\t\t\t\"STENCIL_BACK_PASS_DEPTH_PASS\",\n\t\t\t\"STENCIL_BACK_REF\",\n\t\t\t\"STENCIL_BACK_VALUE_MASK\",\n\t\t\t\"STENCIL_BACK_WRITEMASK\",\n\t\t\t\"STENCIL_BITS\",\n\t\t\t\"STENCIL_BUFFER_BIT\",\n\t\t\t\"STENCIL_CLEAR_VALUE\",\n\t\t\t\"STENCIL_FAIL\",\n\t\t\t\"STENCIL_FUNC\",\n\t\t\t\"STENCIL_INDEX8\",\n\t\t\t\"STENCIL_PASS_DEPTH_FAIL\",\n\t\t\t\"STENCIL_PASS_DEPTH_PASS\",\n\t\t\t\"STENCIL_REF\",\n\t\t\t\"STENCIL_TEST\",\n\t\t\t\"STENCIL_VALUE_MASK\",\n\t\t\t\"STENCIL_WRITEMASK\",\n\t\t\t\"STREAM_DRAW\",\n\t\t\t\"SUBPIXEL_BITS\",\n\t\t\t\"TEXTURE\",\n\t\t\t\"TEXTURE0\",\n\t\t\t\"TEXTURE1\",\n\t\t\t\"TEXTURE10\",\n\t\t\t\"TEXTURE11\",\n\t\t\t\"TEXTURE12\",\n\t\t\t\"TEXTURE13\",\n\t\t\t\"TEXTURE14\",\n\t\t\t\"TEXTURE15\",\n\t\t\t\"TEXTURE16\",\n\t\t\t\"TEXTURE17\",\n\t\t\t\"TEXTURE18\",\n\t\t\t\"TEXTURE19\",\n\t\t\t\"TEXTURE2\",\n\t\t\t\"TEXTURE20\",\n\t\t\t\"TEXTURE21\",\n\t\t\t\"TEXTURE22\",\n\t\t\t\"TEXTURE23\",\n\t\t\t\"TEXTURE24\",\n\t\t\t\"TEXTURE25\",\n\t\t\t\"TEXTURE26\",\n\t\t\t\"TEXTURE27\",\n\t\t\t\"TEXTURE28\",\n\t\t\t\"TEXTURE29\",\n\t\t\t\"TEXTURE3\",\n\t\t\t\"TEXTURE30\",\n\t\t\t\"TEXTURE31\",\n\t\t\t\"TEXTURE4\",\n\t\t\t\"TEXTURE5\",\n\t\t\t\"TEXTURE6\",\n\t\t\t\"TEXTURE7\",\n\t\t\t\"TEXTURE8\",\n\t\t\t\"TEXTURE9\",\n\t\t\t\"TEXTURE_2D\",\n\t\t\t\"TEXTURE_BINDING_2D\",\n\t\t\t\"TEXTURE_BINDING_CUBE_MAP\",\n\t\t\t\"TEXTURE_CUBE_MAP\",\n\t\t\t\"TEXTURE_CUBE_MAP_NEGATIVE_X\",\n\t\t\t\"TEXTURE_CUBE_MAP_NEGATIVE_Y\",\n\t\t\t\"TEXTURE_CUBE_MAP_NEGATIVE_Z\",\n\t\t\t\"TEXTURE_CUBE_MAP_POSITIVE_X\",\n\t\t\t\"TEXTURE_CUBE_MAP_POSITIVE_Y\",\n\t\t\t\"TEXTURE_CUBE_MAP_POSITIVE_Z\",\n\t\t\t\"TEXTURE_MAG_FILTER\",\n\t\t\t\"TEXTURE_MIN_FILTER\",\n\t\t\t\"TEXTURE_WRAP_S\",\n\t\t\t\"TEXTURE_WRAP_T\",\n\t\t\t\"TRIANGLES\",\n\t\t\t\"TRIANGLE_FAN\",\n\t\t\t\"TRIANGLE_STRIP\",\n\t\t\t\"UNPACK_ALIGNMENT\",\n\t\t\t\"UNPACK_COLORSPACE_CONVERSION_WEBGL\",\n\t\t\t\"UNPACK_FLIP_Y_WEBGL\",\n\t\t\t\"UNPACK_PREMULTIPLY_ALPHA_WEBGL\",\n\t\t\t\"UNSIGNED_BYTE\",\n\t\t\t\"UNSIGNED_INT\",\n\t\t\t\"UNSIGNED_SHORT\",\n\t\t\t\"UNSIGNED_SHORT_4_4_4_4\",\n\t\t\t\"UNSIGNED_SHORT_5_5_5_1\",\n\t\t\t\"UNSIGNED_SHORT_5_6_5\",\n\t\t\t\"VALIDATE_STATUS\",\n\t\t\t\"VENDOR\",\n\t\t\t\"VERSION\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_ENABLED\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_NORMALIZED\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_POINTER\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_SIZE\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_STRIDE\",\n\t\t\t\"VERTEX_ATTRIB_ARRAY_TYPE\",\n\t\t\t\"VERTEX_SHADER\",\n\t\t\t\"VIEWPORT\",\n\t\t\t\"ZERO\",\n\t\t\t\"activeTexture\",\n\t\t\t\"attachShader\",\n\t\t\t\"bindAttribLocation\",\n\t\t\t\"bindBuffer\",\n\t\t\t\"bindFramebuffer\",\n\t\t\t\"bindRenderbuffer\",\n\t\t\t\"bindTexture\",\n\t\t\t\"blendColor\",\n\t\t\t\"blendEquation\",\n\t\t\t\"blendEquationSeparate\",\n\t\t\t\"blendFunc\",\n\t\t\t\"blendFuncSeparate\",\n\t\t\t\"bufferData\",\n\t\t\t\"bufferSubData\",\n\t\t\t\"canvas\",\n\t\t\t\"checkFramebufferStatus\",\n\t\t\t\"clear\",\n\t\t\t\"clearColor\",\n\t\t\t\"clearDepth\",\n\t\t\t\"clearStencil\",\n\t\t\t\"colorMask\",\n\t\t\t\"compileShader\",\n\t\t\t\"compressedTexImage2D\",\n\t\t\t\"compressedTexSubImage2D\",\n\t\t\t\"copyTexImage2D\",\n\t\t\t\"copyTexSubImage2D\",\n\t\t\t\"createBuffer\",\n\t\t\t\"createFramebuffer\",\n\t\t\t\"createProgram\",\n\t\t\t\"createRenderbuffer\",\n\t\t\t\"createShader\",\n\t\t\t\"createTexture\",\n\t\t\t\"cullFace\",\n\t\t\t\"deleteBuffer\",\n\t\t\t\"deleteFramebuffer\",\n\t\t\t\"deleteProgram\",\n\t\t\t\"deleteRenderbuffer\",\n\t\t\t\"deleteShader\",\n\t\t\t\"deleteTexture\",\n\t\t\t\"depthFunc\",\n\t\t\t\"depthMask\",\n\t\t\t\"depthRange\",\n\t\t\t\"detachShader\",\n\t\t\t\"disable\",\n\t\t\t\"disableVertexAttribArray\",\n\t\t\t\"drawArrays\",\n\t\t\t\"drawElements\",\n\t\t\t\"drawingBufferHeight\",\n\t\t\t\"drawingBufferWidth\",\n\t\t\t\"enable\",\n\t\t\t\"enableVertexAttribArray\",\n\t\t\t\"finish\",\n\t\t\t\"flush\",\n\t\t\t\"framebufferRenderbuffer\",\n\t\t\t\"framebufferTexture2D\",\n\t\t\t\"frontFace\",\n\t\t\t\"generateMipmap\",\n\t\t\t\"getActiveAttrib\",\n\t\t\t\"getActiveUniform\",\n\t\t\t\"getAttachedShaders\",\n\t\t\t\"getAttribLocation\",\n\t\t\t\"getBufferParameter\",\n\t\t\t\"getContextAttributes\",\n\t\t\t\"getError\",\n\t\t\t\"getExtension\",\n\t\t\t\"getFramebufferAttachmentParameter\",\n\t\t\t\"getParameter\",\n\t\t\t\"getProgramInfoLog\",\n\t\t\t\"getProgramParameter\",\n\t\t\t\"getRenderbufferParameter\",\n\t\t\t\"getShaderInfoLog\",\n\t\t\t\"getShaderParameter\",\n\t\t\t\"getShaderPrecisionFormat\",\n\t\t\t\"getShaderSource\",\n\t\t\t\"getSupportedExtensions\",\n\t\t\t\"getTexParameter\",\n\t\t\t\"getUniform\",\n\t\t\t\"getUniformLocation\",\n\t\t\t\"getVertexAttrib\",\n\t\t\t\"getVertexAttribOffset\",\n\t\t\t\"hint\",\n\t\t\t\"isBuffer\",\n\t\t\t\"isContextLost\",\n\t\t\t\"isEnabled\",\n\t\t\t\"isFramebuffer\",\n\t\t\t\"isProgram\",\n\t\t\t\"isRenderbuffer\",\n\t\t\t\"isShader\",\n\t\t\t\"isTexture\",\n\t\t\t\"lineWidth\",\n\t\t\t\"linkProgram\",\n\t\t\t\"makeXRCompatible\",\n\t\t\t\"pixelStorei\",\n\t\t\t\"polygonOffset\",\n\t\t\t\"readPixels\",\n\t\t\t\"renderbufferStorage\",\n\t\t\t\"sampleCoverage\",\n\t\t\t\"scissor\",\n\t\t\t\"shaderSource\",\n\t\t\t\"stencilFunc\",\n\t\t\t\"stencilFuncSeparate\",\n\t\t\t\"stencilMask\",\n\t\t\t\"stencilMaskSeparate\",\n\t\t\t\"stencilOp\",\n\t\t\t\"stencilOpSeparate\",\n\t\t\t\"texImage2D\",\n\t\t\t\"texParameterf\",\n\t\t\t\"texParameteri\",\n\t\t\t\"texSubImage2D\",\n\t\t\t\"uniform1f\",\n\t\t\t\"uniform1fv\",\n\t\t\t\"uniform1i\",\n\t\t\t\"uniform1iv\",\n\t\t\t\"uniform2f\",\n\t\t\t\"uniform2fv\",\n\t\t\t\"uniform2i\",\n\t\t\t\"uniform2iv\",\n\t\t\t\"uniform3f\",\n\t\t\t\"uniform3fv\",\n\t\t\t\"uniform3i\",\n\t\t\t\"uniform3iv\",\n\t\t\t\"uniform4f\",\n\t\t\t\"uniform4fv\",\n\t\t\t\"uniform4i\",\n\t\t\t\"uniform4iv\",\n\t\t\t\"uniformMatrix2fv\",\n\t\t\t\"uniformMatrix3fv\",\n\t\t\t\"uniformMatrix4fv\",\n\t\t\t\"useProgram\",\n\t\t\t\"validateProgram\",\n\t\t\t\"vertexAttrib1f\",\n\t\t\t\"vertexAttrib1fv\",\n\t\t\t\"vertexAttrib2f\",\n\t\t\t\"vertexAttrib2fv\",\n\t\t\t\"vertexAttrib3f\",\n\t\t\t\"vertexAttrib3fv\",\n\t\t\t\"vertexAttrib4f\",\n\t\t\t\"vertexAttrib4fv\",\n\t\t\t\"vertexAttribPointer\",\n\t\t\t\"viewport\"\n\t\t], \n\t\tconstants : {\n\t\t\tACTIVE_ATTRIBUTES : 35721,\n\t\t\tACTIVE_TEXTURE : 34016,\n\t\t\tACTIVE_UNIFORMS : 35718,\n\t\t\tALIASED_LINE_WIDTH_RANGE : 33902,\n\t\t\tALIASED_POINT_SIZE_RANGE : 33901,\n\t\t\tALPHA : 6406,\n\t\t\tALPHA_BITS : 3413,\n\t\t\tALWAYS : 519,\n\t\t\tARRAY_BUFFER : 34962,\n\t\t\tARRAY_BUFFER_BINDING : 34964,\n\t\t\tATTACHED_SHADERS : 35717,\n\t\t\tBACK : 1029,\n\t\t\tBLEND : 3042,\n\t\t\tBLEND_COLOR : 32773,\n\t\t\tBLEND_DST_ALPHA : 32970,\n\t\t\tBLEND_DST_RGB : 32968,\n\t\t\tBLEND_EQUATION : 32777,\n\t\t\tBLEND_EQUATION_ALPHA : 34877,\n\t\t\tBLEND_EQUATION_RGB : 32777,\n\t\t\tBLEND_SRC_ALPHA : 32971,\n\t\t\tBLEND_SRC_RGB : 32969,\n\t\t\tBLUE_BITS : 3412,\n\t\t\tBOOL : 35670,\n\t\t\tBOOL_VEC2 : 35671,\n\t\t\tBOOL_VEC3 : 35672,\n\t\t\tBOOL_VEC4 : 35673,\n\t\t\tBROWSER_DEFAULT_WEBGL : 37444,\n\t\t\tBUFFER_SIZE : 34660,\n\t\t\tBUFFER_USAGE : 34661,\n\t\t\tBYTE : 5120,\n\t\t\tCCW : 2305,\n\t\t\tCLAMP_TO_EDGE : 33071,\n\t\t\tCOLOR_ATTACHMENT0 : 36064,\n\t\t\tCOLOR_BUFFER_BIT : 16384,\n\t\t\tCOLOR_CLEAR_VALUE : 3106,\n\t\t\tCOLOR_WRITEMASK : 3107,\n\t\t\tCOMPILE_STATUS : 35713,\n\t\t\tCOMPRESSED_TEXTURE_FORMATS : 34467,\n\t\t\tCONSTANT_ALPHA : 32771,\n\t\t\tCONSTANT_COLOR : 32769,\n\t\t\tCONTEXT_LOST_WEBGL : 37442,\n\t\t\tCULL_FACE : 2884,\n\t\t\tCULL_FACE_MODE : 2885,\n\t\t\tCURRENT_PROGRAM : 35725,\n\t\t\tCURRENT_VERTEX_ATTRIB : 34342,\n\t\t\tCW : 2304,\n\t\t\tDECR : 7683,\n\t\t\tDECR_WRAP : 34056,\n\t\t\tDELETE_STATUS : 35712,\n\t\t\tDEPTH_ATTACHMENT : 36096,\n\t\t\tDEPTH_BITS : 3414,\n\t\t\tDEPTH_BUFFER_BIT : 256,\n\t\t\tDEPTH_CLEAR_VALUE : 2931,\n\t\t\tDEPTH_COMPONENT : 6402,\n\t\t\tDEPTH_COMPONENT16 : 33189,\n\t\t\tDEPTH_FUNC : 2932,\n\t\t\tDEPTH_RANGE : 2928,\n\t\t\tDEPTH_STENCIL : 34041,\n\t\t\tDEPTH_STENCIL_ATTACHMENT : 33306,\n\t\t\tDEPTH_TEST : 2929,\n\t\t\tDEPTH_WRITEMASK : 2930,\n\t\t\tDITHER : 3024,\n\t\t\tDONT_CARE : 4352,\n\t\t\tDST_ALPHA : 772,\n\t\t\tDST_COLOR : 774,\n\t\t\tDYNAMIC_DRAW : 35048,\n\t\t\tELEMENT_ARRAY_BUFFER : 34963,\n\t\t\tELEMENT_ARRAY_BUFFER_BINDING : 34965,\n\t\t\tEQUAL : 514,\n\t\t\tFASTEST : 4353,\n\t\t\tFLOAT : 5126,\n\t\t\tFLOAT_MAT2 : 35674,\n\t\t\tFLOAT_MAT3 : 35675,\n\t\t\tFLOAT_MAT4 : 35676,\n\t\t\tFLOAT_VEC2 : 35664,\n\t\t\tFLOAT_VEC3 : 35665,\n\t\t\tFLOAT_VEC4 : 35666,\n\t\t\tFRAGMENT_SHADER : 35632,\n\t\t\tFRAMEBUFFER : 36160,\n\t\t\tFRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 36049,\n\t\t\tFRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 36048,\n\t\t\tFRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 36051,\n\t\t\tFRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 36050,\n\t\t\tFRAMEBUFFER_BINDING : 36006,\n\t\t\tFRAMEBUFFER_COMPLETE : 36053,\n\t\t\tFRAMEBUFFER_INCOMPLETE_ATTACHMENT : 36054,\n\t\t\tFRAMEBUFFER_INCOMPLETE_DIMENSIONS : 36057,\n\t\t\tFRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 36055,\n\t\t\tFRAMEBUFFER_UNSUPPORTED : 36061,\n\t\t\tFRONT : 1028,\n\t\t\tFRONT_AND_BACK : 1032,\n\t\t\tFRONT_FACE : 2886,\n\t\t\tFUNC_ADD : 32774,\n\t\t\tFUNC_REVERSE_SUBTRACT : 32779,\n\t\t\tFUNC_SUBTRACT : 32778,\n\t\t\tGENERATE_MIPMAP_HINT : 33170,\n\t\t\tGEQUAL : 518,\n\t\t\tGREATER : 516,\n\t\t\tGREEN_BITS : 3411,\n\t\t\tHIGH_FLOAT : 36338,\n\t\t\tHIGH_INT : 36341,\n\t\t\tIMPLEMENTATION_COLOR_READ_FORMAT : 35739,\n\t\t\tIMPLEMENTATION_COLOR_READ_TYPE : 35738,\n\t\t\tINCR : 7682,\n\t\t\tINCR_WRAP : 34055,\n\t\t\tINT : 5124,\n\t\t\tINT_VEC2 : 35667,\n\t\t\tINT_VEC3 : 35668,\n\t\t\tINT_VEC4 : 35669,\n\t\t\tINVALID_ENUM : 1280,\n\t\t\tINVALID_FRAMEBUFFER_OPERATION : 1286,\n\t\t\tINVALID_OPERATION : 1282,\n\t\t\tINVALID_VALUE : 1281,\n\t\t\tINVERT : 5386,\n\t\t\tKEEP : 7680,\n\t\t\tLEQUAL : 515,\n\t\t\tLESS : 513,\n\t\t\tLINEAR : 9729,\n\t\t\tLINEAR_MIPMAP_LINEAR : 9987,\n\t\t\tLINEAR_MIPMAP_NEAREST : 9985,\n\t\t\tLINES : 1,\n\t\t\tLINE_LOOP : 2,\n\t\t\tLINE_STRIP : 3,\n\t\t\tLINE_WIDTH : 2849,\n\t\t\tLINK_STATUS : 35714,\n\t\t\tLOW_FLOAT : 36336,\n\t\t\tLOW_INT : 36339,\n\t\t\tLUMINANCE : 6409,\n\t\t\tLUMINANCE_ALPHA : 6410,\n\t\t\tMAX_COMBINED_TEXTURE_IMAGE_UNITS : 35661,\n\t\t\tMAX_CUBE_MAP_TEXTURE_SIZE : 34076,\n\t\t\tMAX_FRAGMENT_UNIFORM_VECTORS : 36349,\n\t\t\tMAX_RENDERBUFFER_SIZE : 34024,\n\t\t\tMAX_TEXTURE_IMAGE_UNITS : 34930,\n\t\t\tMAX_TEXTURE_SIZE : 3379,\n\t\t\tMAX_VARYING_VECTORS : 36348,\n\t\t\tMAX_VERTEX_ATTRIBS : 34921,\n\t\t\tMAX_VERTEX_TEXTURE_IMAGE_UNITS : 35660,\n\t\t\tMAX_VERTEX_UNIFORM_VECTORS : 36347,\n\t\t\tMAX_VIEWPORT_DIMS : 3386,\n\t\t\tMEDIUM_FLOAT : 36337,\n\t\t\tMEDIUM_INT : 36340,\n\t\t\tMIRRORED_REPEAT : 33648,\n\t\t\tNEAREST : 9728,\n\t\t\tNEAREST_MIPMAP_LINEAR : 9986,\n\t\t\tNEAREST_MIPMAP_NEAREST : 9984,\n\t\t\tNEVER : 512,\n\t\t\tNICEST : 4354,\n\t\t\tNONE : 0,\n\t\t\tNOTEQUAL : 517,\n\t\t\tNO_ERROR : 0,\n\t\t\tONE : 1,\n\t\t\tONE_MINUS_CONSTANT_ALPHA : 32772,\n\t\t\tONE_MINUS_CONSTANT_COLOR : 32770,\n\t\t\tONE_MINUS_DST_ALPHA : 773,\n\t\t\tONE_MINUS_DST_COLOR : 775,\n\t\t\tONE_MINUS_SRC_ALPHA : 771,\n\t\t\tONE_MINUS_SRC_COLOR : 769,\n\t\t\tOUT_OF_MEMORY : 1285,\n\t\t\tPACK_ALIGNMENT : 3333,\n\t\t\tPOINTS : 0,\n\t\t\tPOLYGON_OFFSET_FACTOR : 32824,\n\t\t\tPOLYGON_OFFSET_FILL : 32823,\n\t\t\tPOLYGON_OFFSET_UNITS : 10752,\n\t\t\tRED_BITS : 3410,\n\t\t\tRENDERBUFFER : 36161,\n\t\t\tRENDERBUFFER_ALPHA_SIZE : 36179,\n\t\t\tRENDERBUFFER_BINDING : 36007,\n\t\t\tRENDERBUFFER_BLUE_SIZE : 36178,\n\t\t\tRENDERBUFFER_DEPTH_SIZE : 36180,\n\t\t\tRENDERBUFFER_GREEN_SIZE : 36177,\n\t\t\tRENDERBUFFER_HEIGHT : 36163,\n\t\t\tRENDERBUFFER_INTERNAL_FORMAT : 36164,\n\t\t\tRENDERBUFFER_RED_SIZE : 36176,\n\t\t\tRENDERBUFFER_STENCIL_SIZE : 36181,\n\t\t\tRENDERBUFFER_WIDTH : 36162,\n\t\t\tRENDERER : 7937,\n\t\t\tREPEAT : 10497,\n\t\t\tREPLACE : 7681,\n\t\t\tRGB : 6407,\n\t\t\tRGB565 : 36194,\n\t\t\tRGB5_A1 : 32855,\n\t\t\tRGBA : 6408,\n\t\t\tRGBA4 : 32854,\n\t\t\tSAMPLER_2D : 35678,\n\t\t\tSAMPLER_CUBE : 35680,\n\t\t\tSAMPLES : 32937,\n\t\t\tSAMPLE_ALPHA_TO_COVERAGE : 32926,\n\t\t\tSAMPLE_BUFFERS : 32936,\n\t\t\tSAMPLE_COVERAGE : 32928,\n\t\t\tSAMPLE_COVERAGE_INVERT : 32939,\n\t\t\tSAMPLE_COVERAGE_VALUE : 32938,\n\t\t\tSCISSOR_BOX : 3088,\n\t\t\tSCISSOR_TEST : 3089,\n\t\t\tSHADER_TYPE : 35663,\n\t\t\tSHADING_LANGUAGE_VERSION : 35724,\n\t\t\tSHORT : 5122,\n\t\t\tSRC_ALPHA : 770,\n\t\t\tSRC_ALPHA_SATURATE : 776,\n\t\t\tSRC_COLOR : 768,\n\t\t\tSTATIC_DRAW : 35044,\n\t\t\tSTENCIL_ATTACHMENT : 36128,\n\t\t\tSTENCIL_BACK_FAIL : 34817,\n\t\t\tSTENCIL_BACK_FUNC : 34816,\n\t\t\tSTENCIL_BACK_PASS_DEPTH_FAIL : 34818,\n\t\t\tSTENCIL_BACK_PASS_DEPTH_PASS : 34819,\n\t\t\tSTENCIL_BACK_REF : 36003,\n\t\t\tSTENCIL_BACK_VALUE_MASK : 36004,\n\t\t\tSTENCIL_BACK_WRITEMASK : 36005,\n\t\t\tSTENCIL_BITS : 3415,\n\t\t\tSTENCIL_BUFFER_BIT : 1024,\n\t\t\tSTENCIL_CLEAR_VALUE : 2961,\n\t\t\tSTENCIL_FAIL : 2964,\n\t\t\tSTENCIL_FUNC : 2962,\n\t\t\tSTENCIL_INDEX : 6401,\n\t\t\tSTENCIL_INDEX8 : 36168,\n\t\t\tSTENCIL_PASS_DEPTH_FAIL : 2965,\n\t\t\tSTENCIL_PASS_DEPTH_PASS : 2966,\n\t\t\tSTENCIL_REF : 2967,\n\t\t\tSTENCIL_TEST : 2960,\n\t\t\tSTENCIL_VALUE_MASK : 2963,\n\t\t\tSTENCIL_WRITEMASK : 2968,\n\t\t\tSTREAM_DRAW : 35040,\n\t\t\tSUBPIXEL_BITS : 3408,\n\t\t\tTEXTURE : 5890,\n\t\t\tTEXTURE0 : 33984,\n\t\t\tTEXTURE1 : 33985,\n\t\t\tTEXTURE10 : 33994,\n\t\t\tTEXTURE11 : 33995,\n\t\t\tTEXTURE12 : 33996,\n\t\t\tTEXTURE13 : 33997,\n\t\t\tTEXTURE14 : 33998,\n\t\t\tTEXTURE15 : 33999,\n\t\t\tTEXTURE16 : 34000,\n\t\t\tTEXTURE17 : 34001,\n\t\t\tTEXTURE18 : 34002,\n\t\t\tTEXTURE19 : 34003,\n\t\t\tTEXTURE2 : 33986,\n\t\t\tTEXTURE20 : 34004,\n\t\t\tTEXTURE21 : 34005,\n\t\t\tTEXTURE22 : 34006,\n\t\t\tTEXTURE23 : 34007,\n\t\t\tTEXTURE24 : 34008,\n\t\t\tTEXTURE25 : 34009,\n\t\t\tTEXTURE26 : 34010,\n\t\t\tTEXTURE27 : 34011,\n\t\t\tTEXTURE28 : 34012,\n\t\t\tTEXTURE29 : 34013,\n\t\t\tTEXTURE3 : 33987,\n\t\t\tTEXTURE30 : 34014,\n\t\t\tTEXTURE31 : 34015,\n\t\t\tTEXTURE4 : 33988,\n\t\t\tTEXTURE5 : 33989,\n\t\t\tTEXTURE6 : 33990,\n\t\t\tTEXTURE7 : 33991,\n\t\t\tTEXTURE8 : 33992,\n\t\t\tTEXTURE9 : 33993,\n\t\t\tTEXTURE_2D : 3553,\n\t\t\tTEXTURE_BINDING_2D : 32873,\n\t\t\tTEXTURE_BINDING_CUBE_MAP : 34068,\n\t\t\tTEXTURE_CUBE_MAP : 34067,\n\t\t\tTEXTURE_CUBE_MAP_NEGATIVE_X : 34070,\n\t\t\tTEXTURE_CUBE_MAP_NEGATIVE_Y : 34072,\n\t\t\tTEXTURE_CUBE_MAP_NEGATIVE_Z : 34074,\n\t\t\tTEXTURE_CUBE_MAP_POSITIVE_X : 34069,\n\t\t\tTEXTURE_CUBE_MAP_POSITIVE_Y : 34071,\n\t\t\tTEXTURE_CUBE_MAP_POSITIVE_Z : 34073,\n\t\t\tTEXTURE_MAG_FILTER : 10240,\n\t\t\tTEXTURE_MIN_FILTER : 10241,\n\t\t\tTEXTURE_WRAP_S : 10242,\n\t\t\tTEXTURE_WRAP_T : 10243,\n\t\t\tTRIANGLES : 4,\n\t\t\tTRIANGLE_FAN : 6,\n\t\t\tTRIANGLE_STRIP : 5,\n\t\t\tUNPACK_ALIGNMENT : 3317,\n\t\t\tUNPACK_COLORSPACE_CONVERSION_WEBGL : 37443,\n\t\t\tUNPACK_FLIP_Y_WEBGL : 37440,\n\t\t\tUNPACK_PREMULTIPLY_ALPHA_WEBGL : 37441,\n\t\t\tUNSIGNED_BYTE : 5121,\n\t\t\tUNSIGNED_INT : 5125,\n\t\t\tUNSIGNED_SHORT : 5123,\n\t\t\tUNSIGNED_SHORT_4_4_4_4 : 32819,\n\t\t\tUNSIGNED_SHORT_5_5_5_1 : 32820,\n\t\t\tUNSIGNED_SHORT_5_6_5 : 33635,\n\t\t\tVALIDATE_STATUS : 35715,\n\t\t\tVENDOR : 7936,\n\t\t\tVERSION : 7938,\n\t\t\tVERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 34975,\n\t\t\tVERTEX_ATTRIB_ARRAY_ENABLED : 34338,\n\t\t\tVERTEX_ATTRIB_ARRAY_NORMALIZED : 34922,\n\t\t\tVERTEX_ATTRIB_ARRAY_POINTER : 34373,\n\t\t\tVERTEX_ATTRIB_ARRAY_SIZE : 34339,\n\t\t\tVERTEX_ATTRIB_ARRAY_STRIDE : 34340,\n\t\t\tVERTEX_ATTRIB_ARRAY_TYPE : 34341,\n\t\t\tVERTEX_SHADER : 35633,\n\t\t\tVIEWPORT : 2978,\n\t\t\tZERO : 0\n\t\t}\n\t};\n\t\n\tthis.audioContextDescription = {\n\t\tproperties : [\n\t\t\t\"addEventListener\",\n\t\t\t\"audioWorklet\",\n\t\t\t\"baseLatency\",\n\t\t\t\"close\",\n\t\t\t\"createAnalyser\",\n\t\t\t\"createBiquadFilter\",\n\t\t\t\"createBuffer\",\n\t\t\t\"createBufferSource\",\n\t\t\t\"createChannelMerger\",\n\t\t\t\"createChannelSplitter\",\n\t\t\t\"createConstantSource\",\n\t\t\t\"createConvolver\",\n\t\t\t\"createDelay\",\n\t\t\t\"createDynamicsCompressor\",\n\t\t\t\"createGain\",\n\t\t\t\"createIIRFilter\",\n\t\t\t\"createMediaElementSource\",\n\t\t\t\"createMediaStreamDestination\",\n\t\t\t\"createMediaStreamSource\",\n\t\t\t\"createMediaStreamTrackSource\",\n\t\t\t\"createOscillator\",\n\t\t\t\"createPanner\",\n\t\t\t\"createPeriodicWave\",\n\t\t\t\"createScriptProcessor\",\n\t\t\t\"createStereoPanner\",\n\t\t\t\"createWaveShaper\",\n\t\t\t\"currentTime\",\n\t\t\t\"decodeAudioData\",\n\t\t\t\"destination\",\n\t\t\t\"dispatchEvent\",\n\t\t\t\"getOutputTimestamp\",\n\t\t\t\"listener\",\n\t\t\t\"onstatechange\",\n\t\t\t\"outputLatency\",\n\t\t\t\"removeEventListener\",\n\t\t\t\"resume\",\n\t\t\t\"sampleRate\",\n\t\t\t\"state\",\n\t\t\t\"suspend\"\n\t\t], \n\t\tconstants : {}\n\t};\n }\n \n// Node.js init\nif (typeof require !== 'undefined') {\n\tmodule.exports = ContextDescriptor;\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"regpack\",\n    \"version\": \"5.0.4\",\n    \"description\": \"A packer intended for use on minified Javascript code\",\n    \"main\": \"regPack.js\",\n    \"bin\": {\n      \"regpack\": \"bin/regpack\"\n    },\n    \"homepage\": \"https://github.com/Siorki/RegPack\",\n\t\"bugs\" : { \"url\" : \"https://github.com/Siorki/RegPack/issues\" },\n\t\"license\" : \"MIT\",\n\t\"repository\" : \"Siorki/RegPack\",\n    \"dependencies\": {\n        \"minimist\": \">=1.1.0\"\n    }\n}\n"
  },
  {
    "path": "packerData.js",
    "content": "/**\n * @constructor\n * The PackerData class holds actual data :\n *  - string to pack, in the current preprocessing / compression stage\n *  - produced log\n * It also holds internal settings set by the preprocessor\n * and exploited by the packer :\n *  - name of the variable containing the packed code\n *  - generated initialization code\n *  - execution environment\n *  - ..\n *\n * There is one instance for each branch of input (from inputList)\n * as the preprocessing may diverge : different renamings, environments ..\n *\n * @param name The name of the branch, summing the operations performed\n * @param dataString The input string to pack\n */\nfunction PackerData(name, dataString) {\n\tthis.name = name; // trace of the modules that were applied\n\tthis.contents = dataString; // string to pack\n\tthis.log = ''; // log, as shown in the right column in the interactive version\n\tthis.environment = '';\t// execution environment for unpacked code. Can become 'with(...)'\n\tthis.interpreterCall = 'eval(_)';\t// call to be performed on unpacked code.\n\tthis.wrappedInit = '';\t// code inside the unpacked routine\n\tthis.initialDeclarationOffset = 0; // offset for 2D/GL context provided by shim\n\tthis.packedCodeVarName='_'; // name of the variable created to hold the packed code\n\tthis.containedStrings=[]; // all strings inside the input code\n\tthis.containedTemplateLiterals=[]; // all template literals `${...}` inside a string\n\tthis.packedStringDelimiter='\"'; // ', \" or ` around the packed string\n\tthis.thermalMapping=[]; // strings mapping for each compression step, including preprocessing\n\tthis.result= new Array;\n}\n\n\n/**\n * Creates a clone of a PackerData object changing only the name.\n * Copies all member variables, except those added during the packing stage(escapedInput, matchesLookup)\n * Contents and log are copied as well\n *\n * @param packerData Original object to clone\n * @param nameSuffix Suffix appended to the name of the clone\n*/\nPackerData.clone = function(packerData, nameSuffix) {\n\tvar clone = new PackerData;\n\tclone.name = packerData.name + nameSuffix;\n\tclone.contents = packerData.contents;\n\tclone.log = packerData.log;\n\tclone.environment = packerData.environment;\n\tclone.interpreterCall = packerData.interpreterCall;\n\tclone.wrappedInit = packerData.wrappedInit;\n\tclone.initialDeclarationOffset = packerData.initialDeclarationOffset;\n\tclone.packedCodeVarName = packerData.packedCodeVarName;\n\tclone.containedStrings = packerData.containedStrings.slice();\n\tclone.containedTemplateLiterals = packerData.containedTemplateLiterals.slice();\n\tclone.packedStringDelimiter = packerData.packedStringDelimiter;\n\tclone.thermalMapping=packerData.thermalMapping.slice();\n\tclone.result = new Array;\n\treturn clone;\n}\n\n// Node.js init\nif (typeof require !== 'undefined') {\n\tmodule.exports = PackerData;\n}\n"
  },
  {
    "path": "patternViewer.js",
    "content": "/**\n * @constructor\n * The PatternViewer class renders the original code\n * into a DHTML view highlighting the patterns from the packer.\n *\n *\n * @param name The name of the branch, summing the operations performed\n * @param dataString The input string to pack\n */\nfunction PatternViewer ()\n{\n}\n\n\nPatternViewer.prototype = {\n\n\t/**\n\t * Produces an HTML render of the patterns used by the packer.\n\t * The rendered <div> is not added to the page HTML.\n\t * \n\t * @param unpackedCode The original unpacked code (after preprocessing)\n\t * @param matchesLookup Pattern set from RegPack\n\t * @return A <div> object showing the patterns in the code\n\t *\n\t */\n\trender : function(unpackedCode, matchesLookup)\n\t{\n\t\t// First, create arrays storing whether each character is\n\t\t// the beginning or the end of one or several pattern\n\t\tvar patternBegin = [], patternEnd = [];\t// imbrication level for each character\n\t\tfor (var i=0; i<=unpackedCode.length; ++i) {\n\t\t\tpatternBegin.push(0);\n\t\t\tpatternEnd.push(0);\n\t\t}\n\t\tfor (var j=0; j<matchesLookup.length;++j) {\n\t\t\tif (matchesLookup[j].token) {\n\t\t\t\tvar pattern = matchesLookup[j].originalString;\n\t\t\t\tvar offset = -pattern.length;\n\t\t\t\twhile ((offset=unpackedCode.indexOf(pattern, pattern.length+offset))>-1) {\n\t\t\t\t\t++patternBegin[offset];\n\t\t\t\t\t++patternEnd[pattern.length+offset];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tvar output = document.createElement(\"pre\");\n\t\toutput.setAttribute(\"class\",\"topLevel\");\n\t\tvar divStack = [ ];\n\t\tvar currentNodeContents = \"\";\n\t\tvar currentNode = output;\n\t\tvar currentDepth = 0;\n\t\t// #42 : some patterns may contain the very end of the string\n\t\t// (stored in patternEnd[last character + 1] ), so we iterate one extra step to close the matching <div>s\n\t\tfor (var offset=0; offset<=unpackedCode.length; ++offset)\n\t\t{\n\t\t\tfor (var stepsDown=0; stepsDown<patternEnd[offset]; ++stepsDown) {\n\t\t\t\t// unstacking : close the span\n\t\t\t\tif (currentNodeContents != \"\") {\n\t\t\t\t\tcurrentNode.appendChild(document.createTextNode(currentNodeContents));\n\t\t\t\t\tcurrentNodeContents = \"\";\n\t\t\t\t}\n\t\t\t\tcurrentNode = divStack.pop();\n\t\t\t\t--currentDepth;\n\t\t\t}\n\n\t\t\tfor (var stepsUp=0; stepsUp<patternBegin[offset]; ++stepsUp) {\n\t\t\t\t// stacking spans\n\t\t\t\tif (currentNodeContents != \"\") {\n\t\t\t\t\tcurrentNode.appendChild(document.createTextNode(currentNodeContents));\n\t\t\t\t\tcurrentNodeContents = \"\";\n\t\t\t\t}\n\t\t\t\tdivStack.push(currentNode);\n\t\t\t\tvar newSpan = document.createElement(\"span\");\n\t\t\t\tnewSpan.setAttribute(\"class\",\"depth\"+Math.min(9, ++currentDepth));\n\t\t\t\tcurrentNode.appendChild(newSpan);\n\t\t\t\tcurrentNode = newSpan;\n\t\t\t}\n\t\t\t\n\t\t\t// #42 : protect against overflow on that last character\n\t\t\tif (offset<unpackedCode.length) {\n\t\t\t\tcurrentNodeContents+=unpackedCode[offset];\n\t\t\t}\n\t\t}\t\n\t\t\n\t\t// Append the last characters that are not part of a pattern\n\t\tif (currentNodeContents != \"\")\n\t\t\tcurrentNode.appendChild(document.createTextNode(currentNodeContents));\n\n\t\treturn output;\n\t}\n\n}\n\n// Node.js exports (for non-regression tests only)\nif (typeof require !== 'undefined') {\n\tmodule.exports = PatternViewer;\n}\n"
  },
  {
    "path": "regPack.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>RegPack v5.0.4</title>\n\t<style type=\"text/css\">\n#options {\n  border:1px solid gray;\n  padding:5px;\n}\n\n.best {\n  box-shadow: 0px 0px 12px 1px rgb(0, 192, 0) inset;\n}\n\nb {\n\tcolor: black;\n}\n\nb.bestMessage {\n\ttext-shadow: 1px 1px 1px rgb(0, 192, 0)\n}\n\ndiv#output{\n\tdisplay:none;\n}\n\ndiv#thermalColumn{\n\tvisibility:hidden;\n}\n\ndiv.dock {\n\tdisplay:flex;\n\toverflow:hidden;\n}\n\ndiv.dockControl {\n\theight:20px;\n\tmargin:1px;\n\tborder:solid #888 1px;\n\tbox-sizing: border-box;\n\tbackground-image:linear-gradient(#aaa,#ddd,#eee);\n\ttext-align:center;\n\ttext-shadow:#fff 1px 1px;\n\tcolor:#444;\n\tline-height:19px;\n}\n\ndiv.dockControl:hover {\n\tbackground-image:linear-gradient(#adf,#def,#eff);\n}\n\ndiv.dockControl:active {\n\tbackground-image:linear-gradient(#6ce,#bdf,#cef);\n}\n\ndiv.column {\n\tflex:1;\n\theight:15em;\n}\n\ndiv.textBox {\n\twidth:50%;\n\tpadding:1px;\n\tborder:1px solid;\n\tborder-color:#888 #ccc #ccc #888;\n\tmargin: 1px 0px 1px 0px;\n\tbox-sizing: border-box;\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tmin-height:8em;\n}\n\n\ndiv#thermalView {\n\twidth:100%;\n\tpadding:1px;\n\tborder:1px solid;\n\tborder-color:#888 #ccc #ccc #888;\n\tmargin: 1px 0px 1px 0px;\n\tbox-sizing: border-box;\n\tvertical-align: top;\n\theight:14em;\n\toverflow-y:scroll;\n}\n\ntextarea {\n\twidth:50%;\n\tmargin: 1px 0px 1px 0px;\n\tbox-sizing: border-box;\n\tborder-radius:2px;\n\tborder:1px solid;\n\tborder-color:#888 #ccc #ccc #888;\n\tvertical-align: top;\n\tword-break: break-all;\n}\n\ntextarea.source {\n    box-shadow: 0px 0px 8px 1px rgb(0, 128, 255) inset;\n\twidth:100%;\n\theight:17em;\n}\n\npre.topLevel {\n\tmargin:1px;\n\tpadding-top:1px;\n\twhite-space: pre-wrap;\n\tword-break: break-all;\n\tfont: 1em/1.4 monospace;\n}\n\nspan {\n\twhite-space: pre-wrap;\n\tword-break: break-all;\n\tbox-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;\n}\n\nspan.depth1 {\n\tbackground-color:#DFD;\n}\n\nspan.depth2 {\n\tbackground-color:#AEA;\n}\n\nspan.depth3 {\n\tbackground-color:#7D7;\n}\n\nspan.depth4 {\n\tbackground-color:#9C6;\n}\n\nspan.depth5 {\n\tbackground-color:#AB5;\n}\n\nspan.depth6 {\n\tbackground-color:#CA4;\n}\n\nspan.depth7 {\n\tbackground-color:#E93;\n}\n\nspan.depth8 {\n\tbackground-color:#F71;\n}\n\nspan.depth9 {\n\tbackground-color:#F40;\n}\n\nspan.thermal0 {\n\tbackground-color:#64679d;\n}\n\nspan.thermal1 {\n\tbackground-color:#5884cc;\n}\n\nspan.thermal2 {\n\tbackground-color:#6cc3d4;\n}\n\nspan.thermal3 {\n\tbackground-color:#66e18b;\n}\n\nspan.thermal4 {\n\tbackground-color:#a6ec66;\n}\n\nspan.thermal5 {\n\tbackground-color:#f1fc66;\n}\n\nspan.thermal6 {\n\tbackground-color:#fcd966;\n}\n\nspan.thermal7 {\n\tbackground-color:#fcac66;\n}\n\nspan.thermal8 {\n\tbackground-color:#f26464;\n}\n\nspan.thermal9 {\n\tbackground-color:#ce6464;\n}\n\nspan.thermal10 {\n\tbackground-color:#ab6464;\n}\n\nspan.thermal11 {\n\tbackground-color:#886464;\n}\n\n \t</style>\n</head>\n<body>\n<div class=\"dock\">\n<div id=\"inputColumn\" class=\"column\">\n<b>Original source</b>\n<br><textarea id=\"originalString\" class=\"source\"></textarea>\n</div><div id=\"thermalColumn\" class=\"column\">\n<span class=\"thermal0\">0+ </span><span class=\"thermal1\">1+ </span><span class=\"thermal2\">2+ </span><span class=\"thermal3\">3+ </span><span class=\"thermal4\">4+ </span><span class=\"thermal5\">5+ </span><span class=\"thermal6\">6+ </span><span class=\"thermal7\">7+ </span><span class=\"thermal8\">8+ </span><span class=\"thermal9\">9+ </span><span class=\"thermal10\">10+ </span><span class=\"thermal11\">11+ </span>\nbits/original byte\n<div id=\"thermalView\"></div>\n</div>\n</div>\n<div class=\"dockControl\" id=\"inputDockControl\">&#9660;</div><br>\n<fieldset>\nAttempt method hashing and renaming for\n<input type=\"checkbox\" name=\"paramOHash2D\" id=\"paramOHash2D\" checked=\"checked\"/>2D canvas context\n<input type=\"checkbox\" name=\"paramOHashWebGL\" id=\"paramOHashWebGL\" checked=\"checked\"/>WebGL canvas context\n<input type=\"checkbox\" name=\"paramOHashAudio\" id=\"paramOHashAudio\" checked=\"checked\"/>AudioContext\n<input type=\"checkbox\" name=\"paramOHashAllObjects\" id=\"paramOHashAllObjects\" checked=\"checked\"/>any object\n<br><input type=\"checkbox\" name=\"paramOGlobalDefined\" id=\"paramOGlobalDefined\" checked=\"checked\"/> Assume global variable <input type=\"text\" size=6 value=\"c\" id=\"paramOGlobalVariable\" /> is a <select id=\"paramOGlobalType\">\n<option value=0 selected>2D canvas context</option>\n<option value=1>WebGL canvas context</option>\n</select> <i>(as in js1k shim)</i>\n<br><input type=\"checkbox\" name=\"paramOReassignVars\" id=\"paramOReassignVars\" checked=\"checked\"/> Reassign variable names to produce consecutive character blocks,\nexcept for variables <input type=\"text\" value=\"a b c d g\" id=\"paramOExcludedVars\" />\n<br><input type=\"checkbox\" name=\"paramUseES6\" id=\"paramUseES6\" checked=\"checked\"/> Enable ES6 features.\n</fieldset>\n<fieldset>\n<legend><i>Options impacting performance</i></legend>\n<input type=\"checkbox\" name=\"paramOWithMath\" id=\"paramOWithMath\"/> Encapsulate with(Math)\n<br><input type=\"checkbox\" name=\"paramOWrapInSetInterval\" id=\"paramOWrapInSetInterval\"/> Refactor to run with setInterval(). Use variable \n<input type=\"text\" size=4  id=\"paramOTimeVariable\" /> for time <i>(leave empty to assign one. Time variable should be zero on the first loop and nonzero afterwards)</i>.\n</fieldset>\n<fieldset>\n<legend><b class=\"bestMessage\">RegPack v5.0.4</b></legend>\n<button id=\"packAction\">Pack</button>\nScore = <input type=\"text\" size=6 value=1 id=\"paramFGain\" />\n*gain + <input type=\"text\" size=6 value=0 id=\"paramFLength\" />\n*length + <input type=\"text\" size=6 value=0 id=\"paramFCopies\" />\n*copies\n&nbsp;&nbsp;&nbsp;\n\nTiebreaker = <select id=\"paramFTiebreaker\">\n<option value=1 selected>longest string first (Js Crush)</option>\n<option value=-1 >most copies first (First Crush)</option>\n</select>\n<br><i>Default settings match built-in formulas for both JS Crush and First Crush. 2/1/0 sometimes achieve better results.</i>\n</fieldset>\n<br>\n<div id=\"output\">\n<div id=\"preprocessed\">\n\t<b id=\"stage0Title\">Preprocessed : </b><b id=\"stage0Message\"></b>\n\t<br><div id=\"stage0Output\" class=\"textBox\"></div><textarea rows=12 id=\"stage0Details\"></textarea><br>\n</div>\n<div id=\"crushed\">\t\n\t<b id=\"stage1Title\">Crushed : </b><b id=\"stage1Message\"></b><input type=\"checkbox\" id=\"stage1Base64\"/>base64\n\t<br><textarea rows=12 id=\"stage1Output\"></textarea><textarea rows=12 id=\"stage1Details\"></textarea><br>\n</div>\n<div id=\"packed\">\t\n\t<b id=\"stage2Title\">RegPack'ed: </b><b id=\"stage2Message\"></b><input type=\"checkbox\" id=\"stage2Base64\"/>base64\n\t<br><textarea rows=12 id=\"stage2Output\"></textarea><textarea rows=12 id=\"stage2Details\"></textarea><br>\n</div>\n</div>\n<script src=\"contextDescriptor_browser.js\"></script>\n<script src=\"stringHelper.js\"></script>\n<script src=\"packerData.js\"></script>\n<script src=\"shapeShifter.js\"></script>\n<script src=\"regPack.js\"></script>\n<script src=\"patternViewer.js\"></script>\n<script src=\"thermalViewer.js\"></script>\n<script>\n\nvar outputCode = [];\n\nfunction doRegPack() {\n\t\n\tfor (var j=0; j<3; ++j) {\n\t\tdocument.getElementById(\"stage\"+j+\"Output\").value = \"\";\n\t\tdocument.getElementById(\"stage\"+j+\"Details\").value = \"\"\t\n\t}\n\t// Implementation for issue #33 : append the code to the URL\n\tvar input = document.getElementById(\"originalString\").value;\n\tvar urlArray = window.location.href.split(\"#code=\");\n\twindow.location.href = urlArray[0]+\"#code=\"+StringHelper.getInstance().unicodeToBase64(input);\n\t\n\t// Get rid of comments and empty lines\n\tinput = input.replace(/([\\r\\n]|^)\\s*\\/\\/.*|[\\r\\n]+\\s*/g,'');\n\tvar options = {\n\t\twithMath : document.getElementById(\"paramOWithMath\").checked!=\"\",\n\t\thash2DContext : document.getElementById(\"paramOHash2D\").checked!=\"\",\n\t\thashWebGLContext : document.getElementById(\"paramOHashWebGL\").checked!=\"\",\n\t\thashAudioContext : document.getElementById(\"paramOHashAudio\").checked!=\"\",\n\t\thashAllObjects : document.getElementById(\"paramOHashAllObjects\").checked!=\"\",\n\t\tcontextVariableName : document.getElementById(\"paramOGlobalDefined\").checked!=\"\"?document.getElementById(\"paramOGlobalVariable\").value:false,\n\t\tcontextType : parseInt(document.getElementById(\"paramOGlobalType\").value),\n\t\treassignVars : document.getElementById(\"paramOReassignVars\").checked!=\"\",\n\t\tvarsNotReassigned : document.getElementById(\"paramOExcludedVars\").value,\n\t\tcrushGainFactor : parseFloat(document.getElementById(\"paramFGain\").value),\n\t\tcrushLengthFactor : parseFloat(document.getElementById(\"paramFLength\").value),\n\t\tcrushCopiesFactor : parseFloat(document.getElementById(\"paramFCopies\").value),\n\t\tcrushTiebreakerFactor : parseInt(document.getElementById(\"paramFTiebreaker\").value),\n\t\twrapInSetInterval : document.getElementById(\"paramOWrapInSetInterval\").checked!=\"\",\n\t\ttimeVariableName : document.getElementById(\"paramOTimeVariable\").value,\n\t\tuseES6 : document.getElementById(\"paramUseES6\").checked!=\"\"\n\t};\n\n\tvar originalLength = packer.getByteLength(input);\n\tvar inputList = packer.runPacker(input, options);\n\tvar methodCount = inputList.length;\n\tvar patternViewer = new PatternViewer;\n\tvar thermalViewer = new ThermalViewer;\n\t\t\n\tvar bestMethod=0, bestStage=0, bestCompression=1e8;\n\tfor (var i=0; i<methodCount; ++i) {\n\t\tvar packerData = inputList[i];\n\t\tfor (var j=0; j<4; ++j) {\n\t\t\tvar output = (j==0 ? packerData.contents : packerData.result[j-1][1]);\n\t\t\tvar packedLength = packer.getByteLength(output);\n\t\t\tif (packedLength < bestCompression) {\n\t\t\t\tbestCompression = packedLength;\n\t\t\t\tbestMethod = i;\n\t\t\t\tbestStage = j;\n\t\t\t} \n\t\t}\n\t} \n \n\t// show the output boxes (hidden beforehand, they collapsed to near-zero size and this was not pretty)\n\tdocument.getElementById(\"output\").style.display = \"block\";\n\n\tvar bestOutput = inputList[bestMethod];\n\tdocument.getElementById(\"stage0Message\").innerHTML = resultMessage(originalLength, packer.getByteLength(bestOutput.contents));\n\tvar outputField = document.getElementById(\"stage0Output\");\n\twhile (outputField.lastChild) {\n\t\toutputField.removeChild(outputField.lastChild);\n\t}\n\toutputField.appendChild(patternViewer.render(bestOutput.contents, bestOutput.matchesLookup));\n\tdocument.getElementById(\"stage0Details\").value = outputCode[0] = bestOutput.log;\n\tdocument.getElementById(\"stage0Details\").style.height = outputField.offsetHeight+\"px\";\n\n\tfor (var j=1; j<3; ++j) {\n\t\tvar stage = j<2 ? j-1 : (packer.getByteLength(bestOutput.result[1][1])< packer.getByteLength(bestOutput.result[2][1]) ? 1 : 2);\n\t\toutputCode[j] = bestOutput.result[stage][1];\n\t\tvar outputLength = packer.getByteLength(outputCode[j]);\n\t\tdocument.getElementById(\"stage\"+j+\"Title\").setAttribute(\"class\", bestCompression==outputLength ? \"bestMessage\" : \"\");\n\t\tdocument.getElementById(\"stage\"+j+\"Message\").innerHTML = resultMessage(originalLength, outputLength);\n\t\tdocument.getElementById(\"stage\"+j+\"Message\").setAttribute(\"class\", bestCompression==outputLength ? \"bestMessage\" : \"\");\n\t\tdocument.getElementById(\"stage\"+j+\"Base64\").onclick = function(index) {\n\t\t\treturn function() { \n\t\t\t\t// #52 : use StringHelper implementation instead of btoa() to handle Unicode characters > 255\n\t\t\t\tdocument.getElementById(\"stage\"+index+\"Output\").value = this.checked ? StringHelper.getInstance().unicodeToBase64(outputCode[index]) : outputCode[index];\n\t\t\t};\n\t\t} (j);\n\t\tdocument.getElementById(\"stage\"+j+\"Base64\").onclick();\t// set the textarea contents, raw or base64\n\t\t\n\t\t// highlight best result in green\n\t\tdocument.getElementById(\"stage\"+j+\"Output\").setAttribute(\"class\", bestCompression==outputLength ? \"best\" : \"\");\n\t\tdocument.getElementById(\"stage\"+j+\"Details\").value = bestOutput.result[stage][2];\n\t}\n\t\n\t// clear the thermal view, draw and show it\n\tdocument.getElementById(\"thermalColumn\").style.visibility=\"visible\";\n\tvar thermalViewPanel = document.getElementById(\"thermalView\");\n\twhile (thermalViewPanel.firstChild) {\n\t\tthermalViewPanel.removeChild(thermalViewPanel.firstChild);\n\t}\n\tvar completeMapping = bestOutput.thermalMapping;\t// thermal map from preprocessing\n\tif (bestStage > 0) {\n\t\t// thermal map from packing\n\t\tcompleteMapping = completeMapping.concat(bestOutput.result[bestStage-1][3]);\n\t}\n\tthermalViewPanel.appendChild(thermalViewer.render(input, completeMapping));\n\n}\n\n\ndocument.getElementById(\"packAction\").onclick=function()\n{\n\tdoRegPack();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n\t// Fix for issue #12 : disable AudioContext hashing if the browser does not support it\n\tif (typeof AudioContext == \"undefined\") {\n\t\thashAudioCheckbox = document.getElementById(\"paramOHashAudio\");\n\t\thashAudioCheckbox.checked = false;\n\t\thashAudioCheckbox.disabled = true;\n\t}\n\t\n\t// Implementation for issue #33 : retrieve the original code from the URL, if present, and store in into input box\n\tvar urlArray = window.location.href.split(\"#code=\");\n\tif (urlArray.length == 2) {\n\t\tdocument.getElementById(\"originalString\").value = StringHelper.getInstance().base64ToUnicode(urlArray[1]);\n\t}\n \n\t// Following #26, add a dock option for the first row containing input and thermal view \n\tdocument.getElementById(\"inputDockControl\").docked=true;\n\tdocument.getElementById(\"inputDockControl\").onclick = function(event) {\n\t\tthis.docked = !this.docked;\n\t\t\n\t\tvar originalStringView = document.getElementById(\"originalString\");\n\t\tvar originalStringViewHeight=\"17em\";\n\t\tif (!this.docked) {\n\t\t\toriginalStringView.style.height = \"auto\";\n\t\t\toriginalStringViewHeight = (originalStringView.scrollHeight)+\"px\"\n\t\t}\n\t\toriginalStringView.style.height = originalStringViewHeight;\n\t\toriginalStringView.style.overflowY = (this.docked ? \"scroll\" : \"hidden\");\n\t\t\n\t\tdocument.getElementById(\"thermalView\").style.overflowY = this.docked ? \"scroll\" : \"auto\";\n\t\tdocument.getElementById(\"thermalView\").style.height = this.docked ? \"14em\" : \"auto\";\n\t\tdocument.getElementById(\"thermalColumn\").style.height = this.docked ? \"15em\" : \"auto\";\n\t\tdocument.getElementById(\"inputColumn\").style.height = this.docked ? \"15em\" : \"auto\";\n\t\tthis.firstChild.data = this.docked ? \"\\u25bc\" : \"\\u25b2\";\n\t}\n\n});\n\n \n\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "regPack.js",
    "content": "// Node.js : module shapeShifter defines ShapeShifter class (preprocessor)\nif (typeof require !== 'undefined') {\n    var StringHelper = require('./stringHelper');\n    var ShapeShifter = require('./shapeShifter');\n}\nfunction resultMessage(sourceSize, resultSize)\n{\n\tvar message = sourceSize+'B';\n\tvar prefix = (resultSize>sourceSize?\"+\":\"\");\n\tif (sourceSize!=resultSize) {\n\t\tmessage+=' to '+resultSize+'B ('+prefix+(resultSize-sourceSize)+'B, '+prefix+(((resultSize-sourceSize)/sourceSize*1e4|0)/100)+'%)'\n\t}\n\treturn message;\n}\n\n/**\n * Entry point when running RegPack from node.js, wrapper for the packer\n * It performs the packing, then returns the best compressed (autoextractible) string\n *\n * @param input A string containing the program to pack\n * @param options An object detailing the different options for the preprocessor and packer\n * @return A string containing the shortest compressed form of the input\n */\nfunction cmdRegPack(input, options) {\n\n\tvar originalLength = packer.getByteLength(input);\n\tvar inputList = packer.runPacker(input, options);\n\tvar methodCount = inputList.length;\n\n\tvar bestMethod=0, bestStage = 0, shortestLength=1e8;\n\tfor (var i=0; i<methodCount; ++i) {\n\t\tvar packerData = inputList[i];\n\t\tfor (var j=0; j<4; ++j) {\n\t\t\tvar output = (j==0 ? packerData.contents : packerData.result[j-1][1]);\n\t\t\tvar packedLength = packer.getByteLength(output);\n\t\t\tif (packedLength < shortestLength) {\n\t\t\t\tshortestLength = packedLength;\n\t\t\t\tbestMethod = i;\n\t\t\t\tbestStage = j;\n\t\t\t}\n\t\t}\n\t}\n\n\tvar bestOutput = inputList[bestMethod];\n\tvar bestVal = (bestStage==0 ?  bestOutput.contents : bestOutput.result[bestStage-1][1]);\n\tvar mes = resultMessage(originalLength, shortestLength);\n\n\t//console.log(\"packer:\", inputList[bestMethod]);\n\tconsole.warn(\"stats:\", mes);\n\treturn bestVal;\n}\n\n/**\n * @constructor\n * The class RegPack wraps all the features from the tool\n * It contains the main entry point : pack()\n * It also implements the compression routines\n */\nfunction RegPack() {\n\tthis.stringHelper = StringHelper.getInstance();\n\tthis.preprocessor = new ShapeShifter();\n}\n\nRegPack.prototype = {\n\n\n\t/**\n\t * Main entry point for RegPack\n\t * @param input A string containing the program to pack\n\t * @param options An object detailing the different options for the preprocessor and packer\n\t * @return An array of PackerData, each containing the code packed with different settings from the preprocessor\n\t */\n\trunPacker : function(input, options) {\n\n\t\tvar default_options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\thashAllObjects : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\tfor (var opt in default_options) {\n\t\t\tif (!(opt in options)) {\n\t\t\t\toptions[opt] = default_options[opt];\n\t\t\t}\n\t\t}\n\t\tvar inputList = this.preprocessor.preprocess(input, options);\n\t\tfor (var inputIndex=0; inputIndex < inputList.length; ++inputIndex)\n\t\t{\n\t\t\tvar currentData = inputList[inputIndex];\n\n\t\t\t// first stage : configurable crusher\n\t\t\tvar output = this.findRedundancies(currentData, options);\n\t\t\tcurrentData.result.push(output);\n\n\t\t\t// second stage : convert token string to regexp\n\t\t\toutput = this.packToRegexpCharClass(currentData, options);\n\t\t\tcurrentData.result.push(output);\n\n\t\t\t// third stage : try a negated regexp instead\n\t\t\toutput = this.packToNegatedRegexpCharClass(currentData);\n\t\t\tcurrentData.result.push(output);\n\n\n\t\t}\n\t\treturn inputList;\n\t},\n\n\t/**\n\t * Returns the total byte length of a string \"as is\" (with no further escaping)\n\t *  1 for ASCII char\n\t *  3 for Unicode (UTF-8)\n\t * Issue #5 : final size when featuring unicode characters\n\t *\n\t * @param inString the string to measure\n\t * @return the UTF-8 length of the string, in bytes \n\t */\n\tgetByteLength : function (inString)\n\t{\n\t\treturn encodeURI(inString).replace(/%../g,'i').length;\n\t},\n\n\t/**\n\t * Returns the byte length of a string after escaping \n\t *  \\ costs 2 bytes\n\t *  All other characters unchanged\n\t * Issue #85 : suboptimal compression of \\ sequences\n\t *\n\t * @param inString the string to measure\n\t * @return the UTF-8 length of the string, including added escape characters, in bytes \n\t */\n\tgetEscapedByteLength : function (inString)\n\t{\n\t\treturn this.getByteLength(inString.replace(/\\\\/g,'\\\\\\\\'));\n\t},\n\n\t\n\t/**\n\t * First stage : apply the algorithm common to First Crush and JS Crush\n\t * Adds member variables to packerData :\n\t *  - matchesLookup : array containing matches and inner details\n\t *\n\t * @param packerData A PackerData structure holding the input string and setup\n\t * @param options Preprocessing and packing options (tiebreaker, score factors)\n\t * @return array [length, packed string, log]\n\t */\n\tfindRedundancies : function(packerData, options) {\n\t\tvar packedString = packerData.contents;\n\t\tpackerData.matchesLookup = [];\n\t\tvar transform = [];\n\t\tvar details='';\n\t\t// Allowed tokens : everything in the ASCII range except\n\t\t//  - 0 and 127 (excluded from loop)\n\t\t//  - any character present in the input string\n\t\t//  - LF(10) and CR(13), not allowed in code nor as tokens\n\t\t//  - \\ (92) since it requires escaping\n\t\t// New since #55, \"(34) and '(39) are allowed as long as they are not the delimiter for the string\n\t\tvar delimiterCode = packerData.packedStringDelimiter.charCodeAt(0);\n\t\tvar tokenList = [];\n\t\tfor(var tokenCode=126 ; tokenCode>0 ; --tokenCode) {\n\t\t\tif (tokenCode!=10 && tokenCode!=13 && tokenCode!=92 && tokenCode!= delimiterCode) {\n\t\t\t\tvar token = String.fromCharCode(tokenCode);\n\t\t\t\tif (packedString.indexOf(token)==-1) {\n\t\t\t\t\ttokenList.push(token);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Identify matches - search all string space for possible matches (all strings present more than once)\n\t\tvar matches = {};\n\t\tvar found=true;\t// stop as soon as no substring of length t is found twice\n\t\tfor(var t=2;found;++t) {\n\t\t\tfound=false;\n\t\t\tfor(i=0;i<packedString.length-t;++i) {\n\t\t\t\tvar beginCode = packedString.charCodeAt(i);\n\t\t\t\tvar endCode = packedString.charCodeAt(i+t-1);\n\t\t\t\t// #50 : if the first character is a low surrogate (second character of a surrogate pair\n\t\t\t\t// representing an astral character), skip it - we cannot have it begin the string\n\t\t\t\t// and thus break the pair\n\t\t\t\t// Same issue if the last character is a high surrogate (first in surrogate pair).\n\t\t\t\tif ((beginCode<0xDC00 || beginCode>0xDFFF)\n\t\t\t\t\t&& (endCode<0xD800 || endCode>0xDBFF)) {\n\t\t\t\t\tvar j=i;\n\t\t\t\t\tvar pattern = packedString.substr(i,t);\n\t\t\t\t\tif(!matches[pattern]) {\n\t\t\t\t\t\tif(~(j=packedString.indexOf(pattern,j+t)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfound=true;\n\t\t\t\t\t\t\tfor(matches[pattern]=1;~j;matches[pattern]++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tj=packedString.indexOf(pattern,j+t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Main loop : replace matches by tokens, one every iteration\n\t\tvar tokensInUse = '';\n\t\tfor(var tokenIndex = 0; tokenIndex < tokenList.length ; ++tokenIndex) {\n\t\t\tvar token = tokenList[tokenIndex];\n\t\t\n\t\t\t// find the string with the best score (combination of gain, length, copies, and tiebreaker)\n\t\t\tvar bestLength = 0, bestValue = 0, bestMatch = 0, bestGain = 0, bestCopies = 0;\n\t\t\tfor(i in matches){\n\t\t\t\tvar stringLength = this.getEscapedByteLength(i);\n\t\t\t\tvar copies=matches[i];\n\t\t\t\tvar gain=copies*stringLength-copies-stringLength-2;\t// -1 used in JS Crush performs replacement with zero gain\n\t\t\t\tvalue=options.crushGainFactor*gain+options.crushLengthFactor*stringLength+options.crushCopiesFactor*copies;\n\t\t\t\tif(gain>0) {\n\t\t\t\t\tif(value>bestValue||bestValue==value&&(gain>bestGain||gain==bestGain&&(options.crushTiebreakerFactor*copies>options.crushTiebreakerFactor*bestCopies)))  { \n\t\t\t\t\t\t// copies>bestCopies JsCrush, copies<bestCopies First Crush\n\t\t\t\t\t\tbestGain=gain, bestCopies=copies, bestMatch=i, bestValue=value, bestLength=stringLength;\n\t\t\t\t\t}\n\t\t\t\t} \n\t\t\t}\n\n\t\t\tif(bestGain<1)\n\t\t\t\tbreak;\n\n\t\t\t// apply the compression to the string\n\t\t\tpackedString = this.stringHelper.matchAndReplaceAll(packedString, false, bestMatch, token, \"\", token+bestMatch, 0, transform);\n\t\t\t//packedString=packedString.split(bestMatch).join(token)+token+bestMatch;\n\t\t\tpackerData.matchesLookup.push({token:token, string:bestMatch, originalString:bestMatch, depends:'', usedBy:'', gain:bestGain, copies:bestCopies, len:bestLength, score:bestValue, cleared:false, newOrder:9999});\n\t\t\ttokensInUse=token+tokensInUse;\n\t\t\tdetails+=token.charCodeAt(0)+\"(\"+token+\") : val=\"+bestValue+\", gain=\"+bestGain+\", N=\"+bestCopies+\", str = \"+bestMatch+\"\\n\";\n\n\t\t\t// update the other matches in case the selected one is a substring thereof\n\t\t\t// #92 : moved at this point of the loop, since the initial pattern identification is now done before the loop\n\t\t\t// Also performs the final update for #47 so that the remaining matches are known\n\t\t\t// for the packer stage, which may use more tokens than the crusher\n\t\t\tvar newMatches={};\n\t\t\tfor(var oldPattern in matches) {\n\t\t\t\tvar newPattern = oldPattern.split(bestMatch).join(token);\n\t\t\t\tvar newLength = newPattern.length;\n\t\t\t\tif (newLength > 1) { // do not keep the strings already replaced by a token\n\t\t\t\t\tvar offset = packedString.indexOf(newPattern);\n\t\t\t\t\tif (offset >= 0) {\n\t\t\t\t\t\tvar matchCount = 0;\n\t\t\t\t\t\twhile (offset >= 0) {\n\t\t\t\t\t\t\t++matchCount;\n\t\t\t\t\t\t\toffset = packedString.indexOf(newPattern,offset+newLength);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnewMatches[newPattern]=matchCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmatches = newMatches;\n\t\t}\n\t\t\n\n\t\tvar firstLine = true;\n\t\tfor(i in matches){\n\t\t\tvar stringLength = this.getEscapedByteLength(i);\n\t\t\tvar copies=matches[i];\n\t\t\tvar gain=copies*stringLength-copies-stringLength-2;\t\n\t\t\tif (gain>0) {\n\t\t\t\tif (firstLine) {\n\t\t\t\t\tdetails += \"\\n--- Potential gain, but not enough tokens ---\\n\";\n\t\t\t\t\tfirstLine = false;\n\t\t\t\t}\n\t\t\t\tvar value=options.crushGainFactor*gain+options.crushLengthFactor*stringLength+options.crushCopiesFactor*copies;\n\t\t\t\tdetails+=\"..( ) : val=\"+value+\", gain=\"+gain+\", N=\"+copies+\", str = \"+i+\"\\n\";\n\t\t\t\tpackerData.matchesLookup.push({token:\"\", string:i, originalString:i, depends:'', usedBy:'', gain:gain, copies:copies, len:stringLength, score:value, cleared:false, newOrder:9999});\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Implementation for #48 : show the patterns that are \"almost\" gains \n\t\tfirstLine = true;\n\t\tfor(i in matches){\n\t\t\tvar stringLength = this.getEscapedByteLength(i);\n\t\t\tvar copies=matches[i];\n\t\t\tvar gain=copies*stringLength-copies-stringLength-2;\t\n\t\t\tvar gainPlusOne=(copies+1)*stringLength-(copies+1)-stringLength-2;\n\t\t\tif (gain<=0 && gainPlusOne>0) {\n\t\t\t\tif (firstLine) {\n\t\t\t\t\tdetails += \"\\n--- One extra occurrence needed for a gain --\\n\";\n\t\t\t\t\tfirstLine = false;\n\t\t\t\t}\n\t\t\t\tvalue=options.crushGainFactor*gainPlusOne+options.crushLengthFactor*stringLength+options.crushCopiesFactor*copies;\n\t\t\t\tdetails+=\"   val=\"+value+\", gain=\"+gain+\"->\"+gainPlusOne+\" (+\"+(gainPlusOne-gain)+\"), N=\"+copies+\", str = \"+i+\"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t\n\t\tvar loopInitCode = ';for(i in G=', loopMemberCode = 'G[i]';\n\t\tif (options.useES6) {\n\t\t\t// ES6 : use 'for .. of' construct, which iterates on values, not keys => gain six bytes\n\t\t\tloopInitCode = ';for(i of';\n\t\t\tloopMemberCode = 'i';\n\t\t}\n\t\t\n\t\t// escape the backslashes present in the code\n\t\tpackedString = this.stringHelper.matchAndReplaceAll(packedString, false, '\\\\', '\\\\\\\\', '', '', 0, transform); \n\t\t\n\t\t// escape the occurrences of the string delimiter present in the code\n\t\tpackedString = this.stringHelper.matchAndReplaceAll(packedString, false, packerData.packedStringDelimiter, '\\\\'+packerData.packedStringDelimiter, \"\", \"\", 0, transform);\n\t\t//var packedString = packedString.replace(new RegExp(packerData.packedStringDelimiter,\"g\"),'\\\\'+packerData.packedStringDelimiter);\n\t\t\n\t\t// and put everything together\n\t\tvar unpackBlock1 = packerData.packedCodeVarName+'='+packerData.packedStringDelimiter;\n\t\tvar unpackBlock2 = packerData.packedStringDelimiter\n\t\t\t\t\t\t  +loopInitCode+packerData.packedStringDelimiter+tokensInUse+packerData.packedStringDelimiter\n\t\t\t\t\t\t  +')with('+packerData.packedCodeVarName+'.split('+loopMemberCode+'))'\n\t\t\t\t\t\t  +packerData.packedCodeVarName+'=join(pop(';\n\t\tvar unpackBlock3 = '));';\n\t\tvar envMapping = [ { inLength : packedString.length, outLength : packedString.length, complete : false},\n\t\t\t\t\t\t   { chapter  : 1, \n\t\t\t\t\t\t     rangeIn  : [0, packedString.length],\n\t\t\t\t\t\t     rangeOut : [0, unpackBlock1.length + unpackBlock2.length + unpackBlock3.length]\n\t\t\t\t\t\t   }  ];\n\t\ttransform.push(envMapping);\n\t\t\n\t\tvar output = unpackBlock1 + packedString + unpackBlock2 + packerData.wrappedInit + unpackBlock3\n\t\t\t\t   + packerData.environment + packerData.interpreterCall;\n\t\t\n\t\treturn [this.getByteLength(output), output, details, transform];\n\t},\n\n\t/**\n\t * Clears a match from matchesLookup for dependencies in the PackerData\n\t *  - removes the corresponding token from the use list of other matches\n\t *  - sets the \"cleared\" flag to true\n\t *\n\t * @param packerData A PackerData structure holding the input string and setup, including the matches array\n\t * @param matchIndex index of the match to clear\n\t */\n\tclear : function(packerData, matchIndex) {\n\t\tvar oldToken = packerData.matchesLookup[matchIndex].token;\n\t\tfor (var j=0;j<packerData.matchesLookup.length;++j) {\n\t\t\tpackerData.matchesLookup[j].usedBy = packerData.matchesLookup[j].usedBy.split(oldToken).join(\"\");\n\t\t}\n\t\tpackerData.matchesLookup[matchIndex].cleared=true;\n\t},\n\n\t/**\n\t * Second stage : extra actions required to reduce the token string to a RegExp\n\t *\n\t * Needs and modifies the matchesLookup array inside the parameter packerData\n\t *\n\t * @param packerData A PackerData structure holding the input string and setup\n\t * @param options Preprocessing and packing options (tiebreaker, score factors)\n\t * @return array [length, packed string, log]\n\t */\n\tpackToRegexpCharClass : function(packerData, options)\n\t{\n\n\t\tvar details = '';\n\t\tvar transform = [];\n\t\t// First, re-expand the packed strings so that they no longer contain any compression token\n\t\t// since we will be storing them in a different order.\n\t\t// Use this step to establish a dependency graph (compressed strings containing other compressed strings)\n\t\tfor (var i=0;i<packerData.matchesLookup.length;++i) {\n\t\t\tfor (var j=0; j<packerData.matchesLookup.length;++j) {\n\t\t\t\tif (packerData.matchesLookup[i].token && packerData.matchesLookup[j].originalString.indexOf(packerData.matchesLookup[i].token)>-1) {\n\t\t\t\t\tpackerData.matchesLookup[j].originalString = packerData.matchesLookup[j].originalString.split(packerData.matchesLookup[i].token).join(packerData.matchesLookup[i].originalString);\n\t\t\t\t}\n\t\t\t\tif (i!=j && packerData.matchesLookup[j].originalString.indexOf(packerData.matchesLookup[i].originalString)>-1) {\n\t\t\t\t\tpackerData.matchesLookup[j].depends += packerData.matchesLookup[i].token;\n\t\t\t\t\tpackerData.matchesLookup[i].usedBy += packerData.matchesLookup[j].token;\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t/** debug only\n\t\tfor (i=0; i<packerData.matchesLookup.length; ++i) {\n\t\t\tc=packerData.matchesLookup[i];\n\t\t\tdetails += c.token.charCodeAt(0)+\"(\"+c.token+\") str1 = \"+c.string+\" str2 = \"+c.originalString+\" depends = /\"+c.depends+\"/\\n\";\n\t\t}\n\t\t*/\n\n\t\t// Define the token list that will be used by ordering blocks, from the largest to the smallest\n\t\t// Blocks are 4 or more contiguous characters : \"ABCDE\" can be shortened to \"A-E\" in the RegExp\n\t\t// The gain from RegPack v1 over the original JSCrush and First Crush comes essentially from that.\n\t\tvar tokenList = [];\n\t\tvar firstInLine = -1;\n\t\tfor(i=1;i<127;++i) {\n\t\t\tvar token = String.fromCharCode(i);\n\t\t\t// Allowed tokens : everything in the ASCII range except\n\t\t\t//  - 0 and 127 (excluded from loop)\n\t\t\t//  - any character present in the code\n\t\t\t//  - LF(10) and CR(13), not allowed in code nor as tokens, skipped in ranges\n\t\t\t// New since #47, \\(92) and ](93) which need escaping in character class, are allowed as tokens.\n\t\t\t// New since #55, \"(34) and '(39), as long as they are not the delimiter for the string\n\t\t\tif (i!=packerData.packedStringDelimiter.charCodeAt(0) && packerData.contents.indexOf(token)==-1) {\n\t\t\t\tif (firstInLine ==-1) {\n\t\t\t\t\tfirstInLine = i;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (firstInLine >-1) {\n\t\t\t\t\t// do not start a block with CR nor LF, as the first character of the block\n\t\t\t\t\t// needs to be written to the regexp and those are not writable (or require escaping)\n\t\t\t\t\tif (firstInLine == 10 || firstInLine == 13) {\n\t\t\t\t\t\t++firstInLine;\n\t\t\t\t\t}\n\t\t\t\t\tvar lastInLine = i-1;\n\t\t\t\t\t// for the same reason, do not end a block with CR nor LF (watch out, the end of the block is the index before i)\n\t\t\t\t\tif (i==11 || i==14) {\n\t\t\t\t\t\t--lastInLine;\n\t\t\t\t\t}\n\t\t\t\t\tif (lastInLine>=firstInLine) {\t// skip if there is only CR or LF in the range\n\t\t\t\t\t\tvar tokenCount = lastInLine-firstInLine+1;\n\t\t\t\t\t\tvar range = this.stringHelper.writeRangeToRegexpCharClass(firstInLine, lastInLine);\n\t\t\t\t\t\tvar containsBackslash = (firstInLine<=92 && i>92);\n\t\t\t\t\t\ttokenList.push({first:firstInLine, last:lastInLine, count:tokenCount, cost:range.length, oneByteTokenCount:tokenCount-(containsBackslash?1:0)});\n\t\t\t\t\t}\n\t\t\t\t\tfirstInLine = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (firstInLine >-1) {\n\t\t\tvar range = this.stringHelper.writeRangeToRegexpCharClass(firstInLine, i-1);\n\t\t\ttokenList.push({first:firstInLine, last:i-1, count:i-firstInLine, cost:range.length, oneByteTokenCount:i-firstInLine-(firstInLine<=92?1:0)});\n\t\t}\n\t\t// Reorder the block list. #47 introduces escaped characters, making the order a bit more complex :\n\t\t//  - longest blocks (as in largest token count) first \n\t\t//  - when tied for length, non escaped characters first (shortest representation in char class)\n\t\t//  - when tied again, readable characters (32-127) first\n\t\ttokenList.sort(function(a,b) {\n\t\t\treturn 10*b.oneByteTokenCount-b.cost+b.first/1000- (10*a.oneByteTokenCount-a.cost+a.first/1000); \n\t\t});\n\n\t\t\n\t\tvar costOneTokens = [], costTwoTokens = [];\n\t\tfor (var tokenLine=0; tokenLine<tokenList.length; ++tokenLine) {\n\t\t\tfor (var i=tokenList[tokenLine].first; i<=tokenList[tokenLine].last; ++i) {\n\t\t\t\tif (i!=10 && i!=13) {\n\t\t\t\t\tif (i==92) {\n\t\t\t\t\t\tcostTwoTokens.push(i);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcostOneTokens.push(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\t\n\n\t\t\n\t\t// The first range must not start with ^, otherwise it will be interpreted as negated char class\n\t\t// Two solutions :\n\t\t//  - remove the ^ from the range, starting with the next character\n\t\t//  - swap the range with the next one, putting it into second position\n\t\t// Removal is done here if the conditions match (\n\t\t// Swapping is done later, as removal of \\ and ] may expose the character ^ at the beginning of a range\n\t\tif (tokenList[0].first == 94) {\n\t\t\tif (packerData.matchesLookup.length < tokenList[0].count || tokenList.length==1) {\n\t\t\t\t// If we have enough tokens in the first range without the ^, remove it\n\t\t\t\t// Do the same if we only have one range\n\t\t\t\ttokenList[0].cost = this.stringHelper.writeRangeToRegexpCharClass(++tokenList[0].first, ++tokenList[0].last).length;\n\t\t\t\t--tokenList[0].count;\n\t\t\t\t--tokenList[0].oneByteTokenCount;\n\t\t\t} \n\t\t}\n\t\t\n\t\tdetails +=\"\\n\";\n\t\tdetails +=\"Token ranges\\n\"\n\t\tdetails +=\"------------\\n\";\n\t\tfor (var i=0; i<tokenList.length;++i) {\n\t\t\tdetails += this.stringHelper.writeRangeToRegexpCharClass(tokenList[i].first, tokenList[i].last);\n\t\t\tdetails += \" score = \" + ( 10*tokenList[i].oneByteTokenCount-tokenList[i].cost+tokenList[i].first/1000) + \"\\n\";\n\t\t}\n\t\tdetails +=\"\\n\";\n\t\t// Then, flatten the dependency graph into a line. The new compression order starts\n\t\t// with the strings that are not used within another strings (usually the longer ones)\n\t\t// and ends by the strings not depending on others. The reason for that is that the\n\t\t// unpacking is performed LIFO and must begin by independent strings for RegExp-related reasons\n\t\t// (match is done on any token in the RegExp, meaning that the first instance must be\n\t\t// the separator, not another token that would be included in the string)\n\n\t\t// Pack again by replacing the strings by the tokens, in the new compression order\n\t\t// In case there are two or more candidates (not used by other strings), the same\n\t\t// compression scoring is used as in the first stage.\n\t\tvar availableTokens = costOneTokens.concat(costTwoTokens);\n\t\tvar tokensRemaining = true;\t// true while there are tokens remaining\n\t\tvar gainsRemaining = true;\t\t// true while there is at least one match with a positive gain (that will shorten the output if packed)\n\t\tpackerData.tokenCount = 0; // total number of tokens used. Will be less than packerData.matchesLookup.length at the end if any negatives are found\n\t\t\n\t\tvar regPackOutput = packerData.contents;\n\t\tfor (var i=0 ; i<packerData.matchesLookup.length && tokensRemaining && gainsRemaining ; ++i) {\t\t\n\t\t\n\t\t\tvar tokenCode = availableTokens[packerData.tokenCount];\n\t\t\tvar tokenCost = this.stringHelper.getCharacterLength(tokenCode); // 2 for \\ , 1 for anything else\n\t\t\n\t\t\tvar matchIndex=-1, bestScore=-999, bestGain=-1, bestCount=0, negativeCleared = false;\n\t\t\tfor (var j=0; j<packerData.matchesLookup.length;++j) {\n\t\t\t\tif (packerData.matchesLookup[j].usedBy==\"\" && !packerData.matchesLookup[j].cleared) {\n\t\t\t\t\tvar count=0;\n\t\t\t\t\tfor (var index=regPackOutput.indexOf(packerData.matchesLookup[j].originalString, 0); index>-1; ++count) {\n\t\t\t\t\t\tindex=regPackOutput.indexOf(packerData.matchesLookup[j].originalString, index+1);\n\t\t\t\t\t}\n\t\t\t\t\tvar gain = count*(packerData.matchesLookup[j].len-tokenCost)-packerData.matchesLookup[j].len-2*tokenCost;\n\t\t\t\t\tvar score = options.crushGainFactor*gain+options.crushLengthFactor*packerData.matchesLookup[j].len+options.crushCopiesFactor*count;\n\t\t\t\t\tif (gain>=0) {\n\t\t\t\t\t\tif (score>bestScore||score==bestScore&&(gain>bestGain||gain==bestGain&&(options.crushTiebreakerFactor*count>options.crushTiebreakerFactor*bestCount))) { \n\t\t\t\t\t\t\t// R>N JsCrush, R<N First Crush\n\t\t\t\t\t\t\tbestGain=gain,bestCount=count,matchIndex=j,bestScore=score;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// found a negative. The matching string may no longer be packed (if anything, match count will decrease, not increase)\n\t\t\t\t\t\t// so we clear it (ie remove it from the dependency chain). This in turns allows strings it uses to be packed,\n\t\t\t\t\t\t// otherwise their \"usedBy\" field would contain the negative and they could never be packed.\n\t\t\t\t\t\t// Clearing a negative introduces a bias, since some strings that were in order before it could have been\n\t\t\t\t\t\t// considered for compression, but they were not because they were \"usedBy\" the negative.\n\t\t\t\t\t\t// The comparison is useless : do not compress for this iteration of i\n\t\t\t\t\t\tthis.clear(packerData, j);\n\t\t\t\t\t\tnegativeCleared = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!negativeCleared) {\t// skip the compression step if we had a negative\n\t\t\t\tif (matchIndex>-1) {\t\n\t\t\t\t\t// a string was chosen, replace it with the current token\n\t\t\t\t\tvar matchedString = packerData.matchesLookup[matchIndex].originalString;\n\t\t\t\t\tpackerData.matchesLookup[matchIndex].newOrder = packerData.tokenCount;\n\n\t\t\t\t\tvar token = String.fromCharCode(tokenCode);\n\t\t\t\t\tdetails+=token.charCodeAt(0)+\"(\"+token+\"), gain=\"+bestGain+\", N=\"+bestCount+\", str = \"+matchedString+\"\\n\";\n\t\t\t\t\t\n\t\t\t\t\tregPackOutput = this.stringHelper.matchAndReplaceAll(regPackOutput, false, matchedString, token, matchedString+token, \"\", 0, transform);\n\t\t\t\t\t\n\t\t\t\t\t// remove dependencies on chosen string/token\n\t\t\t\t\tthis.clear(packerData, matchIndex);\n\n\t\t\t\t\t// define the replacement token\n\t\t\t\t\t++packerData.tokenCount;\n\t\t\t\t\tif (packerData.tokenCount >= availableTokens.length) {\n\t\t\t\t\t\ttokensRemaining = false; // bail out early\n\t\t\t\t\t\tdetails+=\"Out of tokens\\n\";\n\t\t\t\t\t} \n\t\t\t\t\t\n\n\t\t\t\t} else {\t// remaining strings, but no gain : skip them and end the loop\n\t\t\t\t\tfor (var j=0; j<packerData.matchesLookup.length;++j) {\n\t\t\t\t\t\tif (!packerData.matchesLookup[j].cleared) {\n\t\t\t\t\t\t\tdetails += \"skipped str = \"+packerData.matchesLookup[j].originalString+\"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tgainsRemaining = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Map tokens used to actual ranges (lines)\n\t\t\n\t\tvar tokenLine = 0;\t// 0-based index of current token line (block)\n\t\tvar tokenIndex = 0;\t// 1-based index of current token in current line\n\t\tvar unusedBackslash = availableTokens[availableTokens.length-1]==92 && packerData.tokenCount<availableTokens.length;\n\t\n\t\tif (packerData.tokenCount >= availableTokens.length) { // all available tokens in use (no replacement performed)\n\t\t\ttokenLine = tokenList.length -1 ;\n\t\t\ttokenIndex = tokenList[tokenList.length-1].count;\n\t\t} else {\n\t\t\tvar lastTokenUsed = availableTokens[packerData.tokenCount-1];\n\t\t\tvar lineFound = false;\n\t\t\t\n\t\t\twhile (!lineFound && tokenLine < tokenList.length) {\n\t\t\t\t// #83 : if a range starts or end in \\, and it is not actually used, replace it with the previous or next character\n\t\t\t\tif (unusedBackslash && tokenList[tokenLine].first == 92) { // remove unused \\ at the beginning of a range\n\t\t\t\t\t++tokenList[tokenLine].first;\n\t\t\t\t\t--tokenList[tokenLine].count;\n\t\t\t\t}\n\t\t\t\tif (unusedBackslash && tokenList[tokenLine].last == 92) { // remove unused \\ at the end of a range\n\t\t\t\t\t--tokenList[tokenLine].last;\n\t\t\t\t\t--tokenList[tokenLine].count;\n\t\t\t\t}\t\n\t\t\t\tif (lastTokenUsed >= tokenList[tokenLine].first\t&& lastTokenUsed <= tokenList[tokenLine].last)  {\n\t\t\t\t\t// no need to consider that 2-byte token \\ can be in the middle of a range\n\t\t\t\t\t// since it is used last, and we would be in the case where packerData.tokenCount exceeds costOneTokens.length\n\t\t\t\t\tlineFound = true;\n\t\t\t\t\ttokenIndex = lastTokenUsed - tokenList[tokenLine].first + 1;\n\t\t\t\t} else {\n\t\t\t\t\t++tokenLine;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Safeguard, should never happen (i.e. last token use is not found in ranges)\n\t\tif (tokenLine >= tokenList.length) {\n\t\t\tdetails += \"Exception : token out of range\\nFinal check : failed\";\n\t\t\treturn [-1, \"\", details, transform];\n\t\t}\n\t\t\n\t\t\n\t\t\n\t\t// #47 introduces escaped characters (2 bytes instead of 1) as tokens\n\t\t// Ranges with such a character at the beginning or end thus cost one extra byte to define\n\t\t// If the last range used has leftover tokens (more tokens available than needed),\n\t\t// we use those tokens at no cost to replace the escaped ones.\n\t\t// for instance, last range is A-E but only used up to C, and we have range W-\\\\ before\n\t\t// by substituting D for \\\\, we end up with [W-[A-D] which is one byte shorter than [W-\\\\A-C]\n\t\t\n\t\t// #83 amends that by forcing \\ to be always the last token to be considered\n\t\t// Since it is the last one, there are no leftovers to replace it\n\t\t// The only possible replaced token is thus ]\n\n\t\t// First identify if we have leftover tokens in the last range\n\t\tvar remainingTokens = tokenList[tokenLine].count - tokenIndex;\n\t\t// Force the last range to its actual length - in case it ends with a \\ or ] but not all tokens are used\n\t\ttokenList[tokenLine].last -= remainingTokens; \n\t\ttokenList[tokenLine].count = tokenIndex; \n\t\tif (remainingTokens > 0) {\n\t\t\tvar tokensToReplace = [];\n\t\t\t// then look for escaped character ] (93) at the beginning or end of a range\n\t\t\tfor (var i=0; i<=tokenLine; ++i) {\n\t\t\t\tif (tokenList[i].first == 93) {\n\t\t\t\t\ttokensToReplace.push({rangeIndex : i , atBeginning : true, count : 1});\n\t\t\t\t} else if (tokenList[i].last == 93) {\n\t\t\t\t\ttokensToReplace.push({rangeIndex : i , atBeginning : false, count : 1});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The only token to replace is ] (93), but we kept the code that handles multiple tokens\n\t\t\tfor (var i=0; i<tokensToReplace.length; ++i) {\n\t\t\t\tif (remainingTokens >= tokensToReplace[i].count) {\n\t\t\t\t\t// substitute as many tokens as required (1 or 2)\n\t\t\t\t\tfor (var j=0; j<tokensToReplace[i].count; ++j) {\n\t\t\t\t\t\t// substitute the token in the already packed string\n\t\t\t\t\t\t++tokenIndex;\n\t\t\t\t\t\t--remainingTokens;\n\t\t\t\t\t\tvar currentRange = tokenList[tokensToReplace[i].rangeIndex];\n\t\t\t\t\t\tvar oldToken = String.fromCharCode(tokensToReplace[i].atBeginning ? currentRange.first : currentRange.last);\n\t\t\t\t\t\tvar newToken = String.fromCharCode(++tokenList[tokenLine].last);\n\t\t\t\t\t\tregPackOutput = regPackOutput.split(oldToken).join(newToken);\n\t\t\t\t\t\tdetails += oldToken.charCodeAt(0)+\"(\"+oldToken+\") replaced by \"+newToken.charCodeAt(0)+\"(\"+newToken+\")\\n\";\n\t\t\t\t\t\t\n\t\t\t\t\t\t// shift beginning or end of the former range\n\t\t\t\t\t\t--currentRange.count;\t\n\t\t\t\t\t\t++tokenList[tokenLine].count;\n\t\t\t\t\t\tif (tokensToReplace[i].atBeginning) {\n\t\t\t\t\t\t\t++currentRange.first;\n\t\t\t\t\t\t\t// if the shift exposes an unused \\ at the beginning of the range, shift again\n\t\t\t\t\t\t\tif (unusedBackslash && currentRange.first==92) {\n\t\t\t\t\t\t\t\t++currentRange.first;\n\t\t\t\t\t\t\t\t--currentRange.count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t--currentRange.last;\n\t\t\t\t\t\t\t// if the shift exposes an unused \\ at the end of the range, shift again\n\t\t\t\t\t\t\tif (unusedBackslash && currentRange.last==92) {\n\t\t\t\t\t\t\t\t--currentRange.last;\n\t\t\t\t\t\t\t\t--currentRange.count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// if we are adding from the end of the same range we are removing tokens from, shift beginning while keeping length constant\n\t\t\t\t\t\tif (tokensToReplace[i].rangeIndex == tokenLine) {\n\t\t\t\t\t\t\t--tokenIndex;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\t\n\t\t}\n\n\t\t\n\t\t// The former step may have removed ] and \\ at the beginning of a range, exposing ^ as first character\n\t\t// If the first range starts with ^, the character class will be misinterpreted as a negative char class\n\t\t// Swap the first two ranges to solve that issue.\n\t\tif (tokenList[0].first == 94 && tokenList.length>1) {\n\t\t\tvar newFirstRange = tokenList.splice(1, 1);\n\t\t\ttokenList.unshift(newFirstRange[0]);\n\t\t}\n\t\t\n\t\t// build the character class\n\t\tvar tokenString = this.stringHelper.writeBlocksToRegexpCharClass(tokenList.slice(0, tokenLine+1));\t\t\n\n\t\t// escape the backslashes in the compressed code (from original code or added as token)\n\t\t// #65 : do it now and not earlier so it applies on token \\ as well\n\t\tvar checkedString = this.stringHelper.matchAndReplaceAll(regPackOutput, false, '\\\\', '\\\\\\\\', '', '', 0, transform); \n\t\t\n\t\t// escape the occurrences of the string delimiter present in the code\n\t\tcheckedString = this.stringHelper.matchAndReplaceAll(checkedString, false, packerData.packedStringDelimiter, '\\\\'+packerData.packedStringDelimiter, \"\", \"\", 0, transform);\n\t\t\n\t\t// and add the unpacking code to the compressed string\n\t\tvar unpackBlock1 = 'for('+packerData.packedCodeVarName+'='+packerData.packedStringDelimiter;\n\t\tvar unpackBlock2 = packerData.packedStringDelimiter+';G=/['+tokenString+']/.exec('+packerData.packedCodeVarName\n\t\t\t\t\t\t  +');)with('+packerData.packedCodeVarName+'.split(G))'+packerData.packedCodeVarName+'=join(shift(';\n\t\t\t\t\t\t  \n\t\tvar unpackBlock3 = '));';\n\t\tvar envMapping = [ { inLength : checkedString.length, outLength : checkedString.length, complete : false},\n\t\t\t\t\t\t   { chapter  : 1, \n\t\t\t\t\t\t     rangeIn  : [0, checkedString.length],\n\t\t\t\t\t\t     rangeOut : [0, unpackBlock1.length + unpackBlock2.length + unpackBlock3.length]\n\t\t\t\t\t\t   }  ];\n\t\ttransform.push(envMapping);\n\n\t\tregPackOutput = unpackBlock1 + checkedString + unpackBlock2 + packerData.wrappedInit + unpackBlock3 \n\t\t\t\t\t  + packerData.environment + packerData.interpreterCall;\n \n\t\tvar resultSize = this.getByteLength(regPackOutput);\n\n\t\t// check that unpacking the string yields the original code\n\t\tdetails+=\"------------------------\\nFinal check : \";\n\t\tcheckedString = checkedString.replace(new RegExp('\\\\\\\\'+packerData.packedStringDelimiter,'g'), packerData.packedStringDelimiter);\t\t\n\t\tcheckedString = checkedString.replace(/\\\\\\\\/g, '\\\\');\t\t\n\t\tvar regToken = new RegExp(\"[\"+tokenString+\"]\",\"\");\n\t\tfor(var token=\"\" ; token = regToken.exec(checkedString) ; ) {\n\t\t\tvar k = checkedString.split(token);\n\t\t\tcheckedString = k.join(k.shift());\n\t\t}\n\t\tvar success = (checkedString == packerData.contents);\n\t\tdetails+=(success ? \"passed\" : \"failed\")+\".\\n\";\n\n\n\t\treturn [resultSize, regPackOutput, details, transform];\n\t},\n\n\t/**\n\t * Returns true if the character is not allowed in a RegExp char class or as a token (cannot be inserted per se, requires a sequence instead)\n\t * Forbidden characters are LF, CR, 127\n\t * ' and \" are allowed since #55, unless they are the delimiter for the packed string\n\t *\n\t * @param ascii The ASCII or Unicode value of the character\n\t * @return true if the matching character is forbidden as token, false if it is allowed\n\t */\n\tisForbiddenCharacter : function(ascii)\n\t{\n\t\treturn ascii==10||ascii==13||ascii==127;\n\t},\n\n\t/**\n\t * Returns the number of forbidden characters in the interval [first-last]\n\t * Characters : same as in isForbiddenCharacter(), and the packed string delimiter\n\t * However, the packed string delimiter is forbidden as a token too\n\t *\n\t * @see isForbiddenCharacter\n\t * @param first : ASCII code of the first character in the interval, inclusive\n\t * @param last : ASCII code of the last character in the interval, inclusive\n\t * @param delimiterCode : ASCII code of the packed string delimiter (counts as forbidden)\n\t * @return the number of forbidden characters in the interval\n\t */\n\tcountForbiddenCharacters : function (first, last, delimiterCode)\n\t{\n\t\tvar count=0;\n\t\tfor (var i=first; i<=last; ++i) {\n\t\t\tcount+=(this.isForbiddenCharacter(i)||i==delimiterCode)?1:0;\n\t\t}\n\t\treturn count;\n\t},\n\n\n\t/**\n\t * Third stage : build the shortest negated character class regular expression\n\t * (a char class beginning with a ^, such as [^A-D] which comprises everything but characters A, B, C and D)\n\t * @param packerData A PackerData structure holding the input string and setup\n\t * @return array [length, packed string, log]\n\t */\n\tpackToNegatedRegexpCharClass : function(packerData)\n\t{\n\t\t// Build a list of characters used inside the string (as ranges)\n\t\t// characters not in the list can be\n\t\t//  - forbidden as tokens (LF, CR, 127) although these are allowed in the string too\n\t\t//  - used as compression tokens\n\t\t//  - neither used as compression tokens (if there are leftovers) nor in the string\n\t\t//    those can be included in the RegExp without affecting the output\n\t\tvar details = '';\n\t\tvar transform = [];\n\t\tvar usedCharacters = [];\n\t\tvar forbiddenCharacters = [];\n\t\tvar firstInLine = -1;\n\t\tvar availableCharactersCount = 0;\n\t\tfor(i=1;i<128;++i) {\n\t\t\tvar token = String.fromCharCode(i);\n\t\t\tif (packerData.contents.indexOf(token)>-1) {\n\t\t\t\tif (firstInLine ==-1) {\n\t\t\t\t\tfirstInLine = i;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (firstInLine >-1) {\n\t\t\t\t\tusedCharacters.push({first:firstInLine, last:i-1, size:Math.min(i-firstInLine,3)});\n\t\t\t\t\tfirstInLine = -1;\n\t\t\t\t}\n\t\t\t\tif (this.isForbiddenCharacter(i) || i==packerData.packedStringDelimiter.charCodeAt(0)) {\n\t\t\t\t\tforbiddenCharacters.push(token);\n\t\t\t\t} else {\n\t\t\t\t\t++availableCharactersCount;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (firstInLine >-1) {\n\t\t\tusedCharacters.push({first:firstInLine, last:i-1, size:Math.min(i-firstInLine,3)});\n\t\t}\n\n\t\t// Issue #2 : unicode characters handling\n\t\tvar inputContainsUnicode = false;\n\t\tfor (i=0;i<packerData.contents.length&&!inputContainsUnicode;++i) {\n\t\t\tinputContainsUnicode = inputContainsUnicode || (packerData.contents.charCodeAt(i)>127);\n\t\t}\n\t\tif (inputContainsUnicode) {\n\t\t\t// non-ASCII as a whole block. Those characters are not allowed as tokens,\n\t\t\t// and the block can be merged later to save bytes\n\t\t\tusedCharacters.push({first:128, last:65535, size:3});\n\t\t}\n\n\t\tdetails = availableCharactersCount + \" available tokens, \"+packerData.tokenCount+\" needed.\\n\"\n\t\tvar regExpString = \"\";\n\t\tfor (i in usedCharacters) {\n\t\t\tj=usedCharacters[i];\n\t\t\tvar rangeString = this.stringHelper.writeRangeToRegexpCharClass(j.first, j.last);\n\t\t\t// Fix for issue #31 : if a token line consists in a single \"-\",\n\t\t\t// add it at the beginning of the character class instead of appending it\n\t\t\tif (j.size==1 && j.first==45) { // 45 is '-'\n\t\t\t\tregExpString=rangeString+currentCharClass;\n\t\t\t} else {\n\t\t\t\tregExpString+=rangeString;\n\t\t\t}\n\t\t}\n\t\tdetails+=\"initial expression : \"+regExpString+\"\\n\";\n\n\n\t\t// Now, shorten the regexp by sacrificing some characters that will not be used as tokens.\n\t\t// The second stage yielded the actual number of tokens required.\n\t\t// The initial regexp lists all characters present in the string to compress. Since it is\n\t\t// used with an initial negation ^, it will match on all other characters.\n\t\t// Characters are split into used by the strings, tokens, and unused\n\t\t// This step iterates on the RegExp, merging ranges to reduce its length.\n\t\t// Characters between the ranges are included, thus lost as tokens.\n\t\t// For instance, [A-K] is shorter than [A-CG-K] but loses D,E,F as potential tokens\n\t\t// The process is repeated while there are enough tokens left.\n\t\tvar margin = availableCharactersCount - packerData.tokenCount;\n\t\tvar delimiterCode = packerData.packedStringDelimiter.charCodeAt(0);\n\t\t\n\t\t// #59 : start with merging all ranges with cost 0\n\t\tfor (blockIndex=0; blockIndex<usedCharacters.length-1; ++blockIndex) {\n\t\t\tvar currentBlock = usedCharacters[blockIndex];\n\t\t\tvar nextBlock = usedCharacters[blockIndex+1];\n\t\t\tvar cost = nextBlock.first - currentBlock.last - 1  - this.countForbiddenCharacters(currentBlock.last+1, nextBlock.first-1, delimiterCode);\n\t\t\t\n\t\t\tif (cost < .5) { // equal zero, as it cannot be negative => merge\n\t\t\t\tvar gain = currentBlock.size+nextBlock.size-3;\n\t\t\t\tcurrentBlock.last=nextBlock.last;\n\t\t\t\tcurrentBlock.size=3;\n\t\t\t\tusedCharacters.splice(1+blockIndex, 1);\n\t\t\t\t\n\t\t\t\t// log the improvement\n\t\t\t\tdetails +=\"gain \"+gain+\" for 0, margin = \"+margin+\", \";\n\t\t\t\tvar currentCharClass = this.stringHelper.writeBlocksToRegexpCharClass(usedCharacters);\n\t\t\t\tdetails += currentCharClass+\"\\n\";\n\n\t\t\t}\n\t\t}\n\t\n\t\t// #59 : now test every possibility to bridge gaps by merging ranges\n\t\t// the variable bridgeMap represents, in binary, the gaps that will be bridged in the current round\n\t\t// bit at 0 = leave gap, bit at 1 = bridge by merging neighboring ranges\n\t\t// If that removes more tokens than our margin allows, ignore that candidate and continue to next round\n\t\t// Otherwise, keep the shortest expression of all rounds\n\t\tvar gapCount = usedCharacters.length - 1;\n\t\tvar bestRegExpLength = 127; // length can never exceed 1 byte per character in the ASCII range\n\t\tvar bestRegExpString = \"\";\n\t\tvar bestBlockMap = [];\n\t\tdetails += gapCount+\" gaps, testing \"+(1<<gapCount)+\" solutions.\\n\";\n\t\tfor (var bridgeMap = 0; bridgeMap< (1<<gapCount) ; ++bridgeMap) {\n\t\t\tvar mergedBlocks = [];\n\t\t\tvar totalCost = 0;\n\t\t\tvar blockBegin = usedCharacters[0].first;\n\t\t\tfor (var blockIndex = 0; blockIndex < gapCount; ++blockIndex) {\n\t\t\t\tvar bridgeNext = (bridgeMap & (1<<blockIndex));\n\t\t\t\tvar currentBlock = usedCharacters[blockIndex];\n\t\t\t\tvar nextBlock = usedCharacters[blockIndex+1];\n\t\t\t\tif (bridgeNext) {\n\t\t\t\t\t// if we bridge a gap, keep track of its cost ( = the potential tokens that are lost in the merge)\n\t\t\t\t\tvar cost = nextBlock.first - currentBlock.last - 1  - this.countForbiddenCharacters(currentBlock.last+1, nextBlock.first-1, delimiterCode);\n\t\t\t\t\ttotalCost += cost;\n\t\t\t\t} else {\n\t\t\t\t\t// otherwise, we end a block : add it to the block list, then open a new one\n\t\t\t\t\tmergedBlocks.push( { first: blockBegin, last: currentBlock.last });\n\t\t\t\t\tblockBegin = nextBlock.first;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmergedBlocks.push( { first: blockBegin, last: usedCharacters[usedCharacters.length-1].last });\n\t\t\t\n\t\t\tif (totalCost <= margin) {\n\t\t\t\t// the solution has enough tokens left : now compare it to the current shortest\n\t\t\t\tvar regExpString = this.stringHelper.writeBlocksToRegexpCharClass(mergedBlocks);\n\t\t\t\tif (regExpString.length < bestRegExpLength) {\n\t\t\t\t\tbestRegExpLength = regExpString.length;\n\t\t\t\t\tbestRegExpString = regExpString;\n\t\t\t\t\tbestBlockMap = mergedBlocks.slice();\n\t\t\t\t\tdetails +=\"solution at \"+bestRegExpLength+\", margin = \"+(margin-totalCost)+\", \"+bestRegExpString+\"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\n\t\tbestRegExpString = \"^\"+bestRegExpString;\n\t\tbestBlockMap.push({first:128, last:128});\t// upper boundary for the loop, increase to use multibyte characters as tokens\n\t\tvar tokenString = \"\";\n\t\tvar charIndex = 1;\n\t\tfor (var i=0;i<bestBlockMap.length;++i)\n\t\t{\n\t\t\twhile (charIndex<bestBlockMap[i].first) {\n\t\t\t\tif (!this.isForbiddenCharacter(charIndex) &&  charIndex!=packerData.packedStringDelimiter.charCodeAt(0)) {\n\t\t\t\t\ttokenString+=String.fromCharCode(charIndex);\n\t\t\t\t}\n\t\t\t\t++charIndex;\n\t\t\t}\n\t\t\tcharIndex = 1+bestBlockMap[i].last;\n\t\t}\n\t\tdetails+= \"tokens = \"+tokenString+\" (\"+tokenString.length+\")\\n\";\n\n\t\t// use the same matches order as in the second stage\n\t\tpackerData.matchesLookup.sort(function(a,b) {return a.newOrder-b.newOrder; });\n\t\tvar thirdStageOutput = packerData.contents;\n\t\t// and perform the replacement using the token string as listed above\n\t\tfor (var i=0;i<packerData.tokenCount && i<tokenString.length;++i)\n\t\t{\n\t\t\tvar matchedString = packerData.matchesLookup[i].originalString;\n\t\t\tvar token = tokenString[i];\n\n\t\t\tdetails+=token.charCodeAt(0)+\"(\"+token+\"), str = \"+matchedString+\"\\n\";\n\t\t\tthirdStageOutput = this.stringHelper.matchAndReplaceAll(thirdStageOutput, false, matchedString, token, matchedString+token, \"\", 0, transform);\n\t\t\t//thirdStageOutput = matchedString+token+thirdStageOutput.split(matchedString).join(token);\n\t\t}\n\n\t\t// escape the backslashes in the compressed code (from original code or added as token)\n\t\t// #65 : do it now and not earlier so it applies on token \\ as well\n\t\tvar checkedString = this.stringHelper.matchAndReplaceAll(thirdStageOutput, false, '\\\\', '\\\\\\\\', '', '', 0, transform); \n\n\t\t// escape the occurrences of the string delimiter present in the code\n\t\tcheckedString = this.stringHelper.matchAndReplaceAll(checkedString, false, packerData.packedStringDelimiter, '\\\\'+packerData.packedStringDelimiter, \"\", \"\", 0, transform);\n\n\t\t// add the unpacking code to the compressed string\n\t\tvar unpackBlock1 = 'for('+packerData.packedCodeVarName+'='+packerData.packedStringDelimiter;\n\t\tvar unpackBlock2 = packerData.packedStringDelimiter+';G=/['+bestRegExpString+']/.exec('+packerData.packedCodeVarName\n\t\t\t\t\t\t  +');)with('+packerData.packedCodeVarName+'.split(G))'+packerData.packedCodeVarName+'=join(shift(';\n\t\tvar unpackBlock3 = '));';\n\t\tvar envMapping = [ { inLength : checkedString.length, outLength : checkedString.length, complete : false},\n\t\t\t\t\t\t   { chapter  : 1, \n\t\t\t\t\t\t     rangeIn  : [0, checkedString.length],\n\t\t\t\t\t\t     rangeOut : [0, unpackBlock1.length + unpackBlock2.length + unpackBlock3.length]\n\t\t\t\t\t\t   }  ];\n\t\ttransform.push(envMapping);\n\n\t\tthirdStageOutput = unpackBlock1 + checkedString + unpackBlock2 + packerData.wrappedInit + unpackBlock3 \n\t\t\t\t\t     + packerData.environment + packerData.interpreterCall;\n\t\tvar resultSize = this.getByteLength(thirdStageOutput);\n\n\t\t// check that unpacking the string yields the original code\n\t\tdetails+=\"------------------------\\nFinal check : \";\n\t\tcheckedString = checkedString.replace(new RegExp('\\\\\\\\'+packerData.packedStringDelimiter,'g'), packerData.packedStringDelimiter);\t\t\n\t\tcheckedString = checkedString.replace(/\\\\\\\\/g, '\\\\');\t\t\n\t\tvar regToken = new RegExp(\"[\"+bestRegExpString+\"]\",\"\");\n\t\tfor(var token=\"\" ; token = regToken.exec(checkedString) ; ) {\n\t\t\tvar k = checkedString.split(token);\n\t\t\tcheckedString = k.join(k.shift());\n\t\t}\n\t\tvar success = (checkedString == packerData.contents);\n\t\tdetails+=(success ? \"passed\" : \"failed\")+\".\\n\";\n\n\t\treturn [resultSize, thirdStageOutput, details, transform];\n\t}\n\n};\n\nvar packer = new RegPack();\n\n\n// Node.js setup\nif (typeof require !== 'undefined') {\n    module.exports = {\n        RegPack: RegPack,\n        packer: packer,\n        cmdRegPack: cmdRegPack\n    };\n}\n"
  },
  {
    "path": "shapeShifter.js",
    "content": "// Node.js init\nif (typeof require !== 'undefined') {\n\tvar PackerData = require(\"./packerData\");\n\tvar StringHelper = require(\"./stringHelper\");\n \tvar ContextDescriptor = require(\"./contextDescriptor_node\");\n}\n\n/**\n * @constructor\n * ShapeShifter is the preprocessor used by RegPack.\n * It shortens the code, without compression, by running those algorithms on the original code according to settings\n *  - puts the code in \"with(Math)\" environment\n *  - wraps the main code loop into a call to setInterval()\n *  - hashes method / property names for 2D / GL / Audio contexts\n *  - renames the variable to optimize compression\n */\nfunction ShapeShifter() {\n\t\n\tthis.contextDescriptor = new ContextDescriptor();\n\tthis.stringHelper = StringHelper.getInstance();\n\t\n\t// hashing functions for method and property renaming\n\tthis.hashFunctions = [\n\t\t[\"w[x]\", 0, 2, 0, 0, function(w,x,y) { return w[x]; } ],\n\t\t[\"w[x]+w[y]\", 0, 2, 0, 20, function(w,x,y) { return w[x]+w[y]; } ],\n\t\t[\"w[x]+w.length\", 0, 2, 0, 0, function(w,x,y) { return w[x]+w.length; } ],\n\t\t[\"w[x]+w[w.length-1]\", 0, 2, 0, 0, function(w,x,y) { return w[x]+w[w.length-1]; } ],\n\t\t[\"w[x]+[w[y]]\", 0, 2, 3, 20, function(w,x,y) { return w[x]+[w[y]]; } ],\n\t\t[\"w[0]+w[x]+[w[y]]\", 0, 20, 3, 20, function(w,x,y) { return w[0]+w[x]+[w[y]]; } ],\n\t\t[\"w[1]+w[x]+[w[y]]\", 0, 20, 3, 20, function(w,x,y) { return w[1]+w[x]+[w[y]]; } ],\n\t\t[\"w[2]+w[x]+[w[y]]\", 0, 20, 3, 20, function(w,x,y) { return w[2]+w[x]+[w[y]]; } ],\n\t\t[\"w[0]+[w[x]]+[w[y]]\", 3, 20, 3, 20, function(w,x,y) { return w[0]+[w[x]]+[w[y]]; } ],\n\t\t[\"w.substr(x,3)\", 0, 2, 0, 0, function(w,x,y) { return w.substr(x,3); } ]\n\t];\n\t\n}\n\n\nShapeShifter.prototype = {\n\n\t\n\t/**\n\t * Preparation stage : attempt to rehash the methods from canvas context\n\t * Produces a pair of hashed/not hashed strings for each option\n\t * so each selected \"splitter\" flag doubles the number of tests. \n\t * Creates a list with all the combinations to feed to the packer, one at a time.\n\t * \"with Math()\" option is applied on all entries if selected (does not create a pair)\n\t *\n\t * @param input : the string to pack\n\t * @param options : preprocessing options, as follows\n\t *       -  withMath : true if the option \"Pack with(Math)\" was selected, false otherwise\n\t *       -  hash2DContext : (splitter) true if the option \"Hash and rename 2D canvas context\" was selected, false otherwise\n\t *       -  hashWebGLContext : (splitter) true if the option \"Hash and rename WebGL canvas context\" was selected, false otherwise\n\t *       -  hashAudioContext : (splitter) true if the option \"Hash and rename AudioContext\" was selected, false otherwise\n\t *       -  hashAllObjects : (splitter) true if the option \"Hash and rename properties for any object\" was selected, false otherwise\n\t *       -  contextVariableName : a string representing the variable holding the context if the \"assume context\" option was selected, false otherwise\n\t *       -  contextType : the context type (0=2D, 1=WebGL) if the \"assume context\" option was selected, irrelevant otherwise\n\t *       -  reassignVars : true to globally reassign variable names \n\t *       -  varsNotReassigned : string or array listing all protected variables (whose name will not be modified)\n\t *       -  wrapInSetInterval : true to wrap the unpacked code in a setInterval() call instead of eval()\n\t *       -  timeVariableName : if \"setInterval\" option is set, the variable to use for time (zero on first loop, nonzero after)\n\t *       -  useES6 : true to add ES6 constructs to the code, false otherwise\n\t * @return an array of PackerData, representing all combinations for splitter flags  \n\t */\n\tpreprocess : function(input, options) {\n\t\n\t\t// Transform the list of protected variables into a boolean array[128] (true = protected).\n\t\t// Same information but easier to access by algorithms.\n\t\t// Only perform it once on an option set : unit tests reuse the same options for several calls\n\t\tif (!options.varsNotReassignedRaw) {\n\t\t\toptions.varsNotReassignedRaw = options.varsNotReassigned;\n\t\t\toptions.varsNotReassigned = [];\n\t\t\tfor (var i=0; i<128; ++i) {\t// replace by Array.fill() once ES6 is supported\n\t\t\t\toptions.varsNotReassigned.push(false);\n\t\t\t}\n\t\t\tfor (var i=0; i<options.varsNotReassignedRaw.length; ++i) {\n\t\t\t\tvar ascii = options.varsNotReassignedRaw[i].charCodeAt(0);\n\t\t\t\tif (ascii>=0 && ascii<128 && this.isCharAllowedInVariable(ascii)) {\n\t\t\t\t\toptions.varsNotReassigned[ascii] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// #74 , #96 : minification now performed inside the preprocessor, not as a hidden step in the main entry point\n\t\tvar minifiedInput = this.minify(input);\n\t\t\n\t\tvar inputData = new PackerData ('', minifiedInput);\n\t\tif (options.withMath) {\n\t\t\t// call module : Define environment\n\t\t\tthis.defineEnvironment(inputData);\n\t\t}\n\t\t\n\t\tvar inputList = [ inputData ];\n\t\tif (options.wrapInSetInterval) {\n\t\t\t// call module : wrap with setInterval\n\t\t\tthis.refactorToSetInterval(inputData, options);\n\t\t} else {\n\t\t\t// map the bytes of the default interpreter call \"eval(_)\" to the entire code\n\t\t\tvar envMapping = [ { inLength : inputData.contents.length, outLength : inputData.contents.length, complete : false},\n\t\t\t\t   { chapter  : 4, \n\t\t\t\t\t rangeIn  : [0, inputData.contents.length],\n\t\t\t\t\t rangeOut : [0, inputData.interpreterCall.length]\n\t\t\t\t   }  ];\n\t\t\tinputData.thermalMapping.push(envMapping);\n\n\t\t}\n\t\t\n\t\t\n\t\t// Hash and rename methods of the 2d canvas context\n\t\t//  - method hashing only\n\t\t//  - method and property\n\t\t// then store the results in the inputList\n\t\tif (options.hash2DContext) {\n\t\t\tfor (var count=inputList.length, i=0; i<count; ++i) {\n\t\t\t\tvar newBranches = this.preprocess2DContext(inputList[i], options);\n\t\t\t\tinputList.push(...newBranches); // ES6 syntax : concatenate arrays\n\t\t\t}\n\t\t}\n\t\t\n\t\t// for WebGL contexts, there are three options \n\t\t//   - hash and replace method names only \n\t\t//   - as above, plus replace the definitions of constants with their values (magic numbers)\n\t\t//   - hash and replace method and property names\n\t\tif (options.hashWebGLContext) {\n\t\t\tfor (var count=inputList.length, i=0; i<count; ++i) {\n\t\t\t\tvar newBranches = this.preprocessWebGLContext(inputList[i], options);\n\t\t\t\tinputList.push(...newBranches); // ES6 syntax : concatenate arrays\n\t\t\t}\n\t\t}\n\n\t\t// for AudioContexts, method hashing only\n\t\tif (options.hashAudioContext) {\n\t\t\tfor (var count=inputList.length, i=0; i<count; ++i) {\n\t\t\t\tvar newBranches = this.preprocessAudioContext(inputList[i], options.varsNotReassigned);\n\t\t\t\tinputList.push(...newBranches); // ES6 syntax : concatenate arrays\n\t\t\t}\n\t\t}\n\t\t\n\t\t// #86 : for all objects, using a variable as container for the method or property name\n\t\tif (options.hashAllObjects) {\n\t\t\tfor (var count=inputList.length, i=0; i<count; ++i) {\n\t\t\t\tvar newBranchData = this.hashPropertyNamesAsVariables(inputList[i], options);\n\t\t\t\tif (newBranchData[0]) { // true if at least one property was assigned to a variable\n\t\t\t\t\tinputList.push(newBranchData[1]); \n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tinputList[0].name=\"unhashed\";\n\n\t\tfor (var i=0; i<inputList.length; ++i) {\n\t\t\tthis.identifyStrings(inputList[i], false);\n\t\t\t// call module : quote strings\n\t\t\tthis.quoteStrings(inputList[i], options);\n\t\t\tif (options.reassignVars) {\n\t\t\t\t// call module : reassign variables\n\t\t\t\tthis.reassignVariableNames(inputList[i], options);\n\t\t\t}\n\t\t}\n\t\treturn inputList;\n\t},\n\n\t/**\n\t * Performs a light minification on the input string\n\t * \n\t * Removes C-style comments\n\t * Removes C++ one-line style comments\n\t * Removes spaces, tabs, CR, CR LF\n\t * Replaces ;} with }\n\t * \n\t * Exception : any string defined inside the program is not modified\n\t * \n\t * @param input : the string (program) to minify\n\t * @return the same, minified\n\t */\n\tminify : function(input) {\n\t\tvar output = \"\";\n\t\tvar previousCharCode = 0;\n\t\tvar closingSequence = \"\";\n\t\tvar inRemovedSequence = false, inString = false;\n\t\t\n\t\tfor (let i=0; i<input.length; ++i) {\n\t\t\tlet currentChar = input[i];\n\t\t\tlet nextCharCode = i<input.length-1 ? input.charCodeAt(i+1) : 0;\n\t\t\tlet testForSequenceEnd = true;\n\t\t\tif (closingSequence == \"\") { \n\t\t\t\t// no sequence in progress, detect a beginning\n\t\t\t\ttestForSequenceEnd = false;\n\t\t\t\tif (currentChar == \"'\" || currentChar == '\"' || currentChar == '`') {\n\t\t\t\t\t// #96 : leave string contents untouched, no whitespace removal\n\t\t\t\t\tclosingSequence = currentChar;\n\t\t\t\t\tinString = true;\n\t\t\t\t}\n\t\t\t\tif (input.substr(i,2) == \"//\") { // C++ style single-line comment\n\t\t\t\t\tclosingSequence = \"\\n\";\n\t\t\t\t\tinRemovedSequence = true;\n\t\t\t\t}\n\t\t\t\tif (input.substr(i,2) == \"/*\") { // C multi-line comment\n\t\t\t\t\tclosingSequence = \"*/\";\n\t\t\t\t\tinRemovedSequence = true;\n\t\t\t\t}\n\t\t\t\tif (input.substr(i,4) == \"<!--\") { // XML multi-line comment\n\t\t\t\t\tclosingSequence = \"-->\";\n\t\t\t\t\tinRemovedSequence = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!inRemovedSequence) {\n\t\t\t\tlet isBlank = \"\\n\\r\\t \".indexOf(currentChar) > -1;\n\t\t\t\t// all non-blanks are copied\n\t\t\t\t// #74 : so are all blanks between expressions or keywords\n\t\t\t\t// multiple blanks between expressions are shortened, only the last one is kept\n\t\t\t\tlet doCopy = (!isBlank) || (this.isCharAllowedInVariable(previousCharCode) && this.isCharAllowedInVariable(nextCharCode));\n\t\t\t\t//console.log(currentChar.charCodeAt(0)+\" (\"+currentChar+\") \"+doCopy);\n\t\t\t\tif (doCopy || inString) { // #96 : no change inside a string\n\t\t\t\t\toutput+=currentChar;\n\t\t\t\t\tpreviousCharCode = currentChar.charCodeAt(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (testForSequenceEnd) {\n\t\t\t\tif (input.substr(i,closingSequence.length) == closingSequence)\n\t\t\t\t{\n\t\t\t\t\ti+= closingSequence.length-1;\n\t\t\t\t\tclosingSequence = \"\";\n\t\t\t\t\tinRemovedSequence = false;\n\t\t\t\t\tinString = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Remove any semicolon located right before a block ends\n\t\toutput = output.replace(/;}/g, \"}\");\n\t\treturn output;\n\t},\n\t\n\t/**\n\t * Modifies the environment execution of the unpacked code, wrapping it into with(Math).\n\t * Removes all references to Math. in the input code\n\t * @param inputData (in/out) PackerData structure containing the code to refactor and setup \n\t * @return nothing. Result of refactoring is stored in parameter inputData.\n\t */\n\tdefineEnvironment : function(inputData) {\n\t\tinputData.environment = 'with(Math)';\n\t\tvar envMapping = { chapter : 2, rangeOut : [0, inputData.environment.length] };\n\t\tinputData.contents = this.stringHelper.matchAndReplaceAll(inputData.contents, false, 'Math.', '', '', '', envMapping, inputData.thermalMapping);\n\t},\n\t\n\t\n\t/**\n\t * Rewrites the input code so that it can entirely be executed inside\n\t * a setInterval() loop without prior initialization.\n\t *\n\t * Detects the function that is currently called through setInterval()\n\t * and strips it. Wraps the code before that function into a if sequence\n\t * at the beginning of the loop, that will only be run once.\n\t *\n\t * The method makes use of a \"time\" variable that usually controls\n\t * the flow of execution/rendering, and is increased at each loop.\n\t * It needs to be zero on the first run to trigger the initialization\n\t * sequence, then nonzero on the subsequent runs.\n\t *\n\t * Output (refactored code and log) is stored in parameter inputData.\n\t * Setup for the unpacking routine is also changed in the same object.\n\t * \n\t * @param inputData (in/out) PackerData structure containing the code to refactor and setup \n\t * @param options options set, see below for use details\n\t * @return nothing. Result of refactoring is stored in parameter inputData.\n\t * Options used are :\n\t *  - timeVariableName : the variable containing time, or empty string to allocate one\n\t *  - varsNotReassigned : boolean array[128], true to avoid altering variable\n\t * \n\t */\n\trefactorToSetInterval : function(inputData, options) {\n\t\tvar input = inputData.contents;\n\t\tvar output = input; // initialized from input, in case we bail out early\n\t\tvar timeVariableName = options.timeVariableName;\n\t\tvar varsNotReassigned = options.varsNotReassigned;\n\t\tvar details = \"----------- Refactoring to run with setInterval() ---------------\\n\";\n\t\tvar timeVariableProvided = true;\n\n\t\t// implementation for #44 : match arrow function syntax (new in ES6)\t\t\n\t\t// regular expression matches pre-ES5 syntax : function(params){...}\n\t\tvar loopMatch = input.match(/setInterval\\(function\\(([\\w\\d.=,]*)\\){/);\n\t\tvar functionDeclaration = \"function(\";\n\t\tif (!loopMatch) { // regular expression matches ES6 syntax : (params)=>{...}\n\t\t\tloopMatch = input.match(/setInterval\\(\\(([\\w\\d.=,]*)\\)=>{/);\n\t\t\tfunctionDeclaration = \")=>\";\n\t\t}\n\t\tif (!loopMatch) { // regular expression matches ES6 syntax : one_param=>{...}\n\t\t\tloopMatch = input.match(/setInterval\\(([\\w\\d.]*)(=>){/);\n\t\t\tfunctionDeclaration = \"=>\";\n\t\t}\n\n\t\tif (loopMatch) {\n\t\t\tvar initCode = input.substr(0, loopMatch.index);\n\t\t\t// remove any trailing comma or semicolon\t\t\t\n\t\t\tif (initCode[initCode.length-1]==';' || initCode[initCode.length-1]==',') {\n\t\t\t\tinitCode = input.substr(0, initCode.length-1);\n\t\t\t}\n\t\t\t\n\t\t\tdetails += \"First \"+loopMatch.index+\" bytes moved to conditional sequence.\\n\";\n\t\t\t\n\t\t\t// parameters of the function passed to setInterval() : extract default values\n\t\t\t// The regex matches a variable declaration, without value assignment (no \"=\"),\n\t\t\t// at the beginning, end, or between two commas\n\t\t\tvar paramsCode = loopMatch[1];\n\t\t\tvar paramsExp = /(^|,)[A-Za-z$_][\\w$_]*(,|$)/;\n\t\t\n\t\t\tvar paramsMatch = paramsExp.exec(paramsCode);\n\t\t\twhile (paramsMatch && paramsMatch[0] != \"\") {\n\t\t\t\t// if the variable is between two commas, keep one : \",k,\" becomes \",\"\n\t\t\t\tvar keptCommas = (paramsMatch[1]+paramsMatch[2]).length>1 ? \",\" : \"\";\n\t\t\t\tparamsCode = paramsCode.substr(0, paramsMatch.index)+keptCommas+paramsCode.substr(paramsMatch.index+paramsMatch[0].length);\n\t\t\t\tparamsMatch = paramsExp.exec(paramsCode);\n\t\t\t}\n\n\t\t\t// end by a semicolon if there are any initializations that will be added to the main loop code\n\t\t\tparamsCode += (paramsCode != \"\" ? \";\" : \"\");\n\t\t\t\n\t\t\tif (timeVariableName==\"\") {\n\t\t\t\ttimeVariableProvided = false;\n\t\t\t\ttimeVariableName = this.allocateNewVariable(inputData, options);\n\t\t\t\tdetails += \"Using variable \"+timeVariableName+\" for time.\\n\";\n\t\t\t}\n\t\t\t\n\t\t\t// Strip the declaration of the time variable from the init code,\n\t\t\t// as it will be defined in the unpacking routine instead.\n\t\t\tvar timeDefinitionBegin = initCode.length;\n\t\t\tvar timeDefinitionEnd = timeDefinitionBegin;\n\t\t\tvar timeDefinitionExp = new RegExp(\"(^|[^\\\\w$])\"+timeVariableName+\"=\",\"g\");\n\t\t\tvar timeDefinitionMatch=timeDefinitionExp.exec(initCode);\n\t\t\tif (timeDefinitionMatch) {\n\t\t\t\ttimeDefinitionBegin = timeDefinitionMatch.index+timeDefinitionMatch[1].length;\n\t\t\t\ttimeDefinitionEnd = timeDefinitionBegin+2;\n\t\t\t\t\n\t\t\t\t// Check if we can strip more than \"t=\" depending on what comes before and after :\n\t\t\t\t//  - Brackets means no : the declaration is used as an argument in a function\n\t\t\t\t//  - Square brackets means no : used to define an array\n\t\t\t\t//  - Both commas before and after : same context, inside function or array\n\t\t\t\t//  - a leading = means no : multiple variables are defined at the same time\n\t\t\t\t//  - anything other than \"0\" after is no\n\t\t\t\t//  - other configurations are ok to remove up to the separator\n\t\t\t\tvar canRemoveInitValue = true;\n\t\t\t\tvar leadingChar = timeDefinitionBegin>0 ? initCode[timeDefinitionBegin-1] : \"\";\n\t\t\t\tvar trailingChar = initCode[timeDefinitionEnd];\n\t\t\t\tvar furtherChar = timeDefinitionEnd+1<initCode.length ? initCode[timeDefinitionEnd+1] : \"\";\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (leadingChar != \"=\");\t// multiple variable init\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (trailingChar == \"0\");\t// multiple variable init, or function call\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (leadingChar != \"(\"); \t// used as a function argument\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (leadingChar != \"[\"); \t// used as array member\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (furtherChar != \")\"); \t// used as a function argument\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && (furtherChar != \"]\"); \t// used as array member\n\t\t\t\tcanRemoveInitValue = canRemoveInitValue && !(leadingChar == \",\" && furtherChar == \",\"); \t// used as a function argument\n\t\t\t\ttimeDefinitionEnd+=(canRemoveInitValue?1:0);\n\t\t\t\ttimeDefinitionEnd+=(canRemoveInitValue&&furtherChar==\";\"?1:0);\n\t\t\t\ttimeDefinitionEnd+=(canRemoveInitValue&&furtherChar==\",\"&&leadingChar==\"\"?1:0);\n\t\t\t\ttimeDefinitionBegin+=(canRemoveInitValue&&furtherChar!=\";\"&&leadingChar==\";\"?-1:0);\n\t\t\t\ttimeDefinitionBegin+=(canRemoveInitValue&&furtherChar==\"\"&&leadingChar==\",\"?-1:0);\n\t\t\t\t\n\t\t\t\tdetails += \"Removed declaration \\\"\"+initCode.substr(timeDefinitionBegin, timeDefinitionEnd-timeDefinitionBegin)+\"\\\"\\n\";\n\t\t\t\tinitCode = initCode.substr(0,timeDefinitionBegin)+initCode.substr(timeDefinitionEnd);\n\t\t\t}\n\n\n\t\t\t\n\t\t\tvar inString = 0, bracketDepth = 1, index = loopMatch.index+loopMatch[0].length;\n\t\t\twhile (bracketDepth>0 && index<input.length)\n\t\t\t{\n\t\t\t\tif (inString == 0) {\n\t\t\t\t\tswitch (input.charCodeAt(index)) {\n\t\t\t\t\t\tcase 34 : // \"\n\t\t\t\t\t\tcase 39 : // '\n\t\t\t\t\t\tcase 96 : // `\n\t\t\t\t\t\t\tinString = input.charCodeAt(index);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 123 : // {\n\t\t\t\t\t\t\t++bracketDepth;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 125 : // }\n\t\t\t\t\t\t\t--bracketDepth;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else if (input.charCodeAt(index) == inString && inString>0) {\n\t\t\t\t\tinString = 0;\n\t\t\t\t}\n\t\t\t\t++index;\n\t\t\t}\n\t\t\tvar finalCode = input.substr(index);\n\t\t\tvar delayMatch = finalCode.match(/,([\\w\\d.=]*)\\);?/);\n\t\t\tif (delayMatch) {\n\t\t\t\tfinalCode = finalCode.substr(delayMatch[0].length);\n\t\t\t\tif (finalCode.length) {\n\t\t\t\t\tdetails += \"Last \"+finalCode.length+\" bytes also moved there.\\n\";\n\t\t\t\t\tfinalCode = (initCode.length > 0 ? \";\" : \"\")+finalCode;\n\t\t\t\t}\n\t\t\t\tdetails += \"Interval of \"+delayMatch[1]+ \"ms pushed to unpacking routine.\\n\";\n\t\t\t\t\n\t\t\t\t// wrap the initialization code into a conditional sequence :\n\t\t\t\t//   - if(!t){/*init code*/} if the variable is used (and set) afterwards\n\t\t\t\t//   - if(!t++){/*init code*/} if it is created only for the test\n\t\t\t\t//   -  #72 : nothing if there is no init code \n\t\t\t\tvar wrapperCode = \"\", wrapperEnd = \"\";\n\t\t\t\tif (initCode.length + finalCode.length > 0) {\n\t\t\t\t\twrapperCode = \"if(!\"+timeVariableName+(timeVariableProvided?\"\":\"++\")+\"){\";\n\t\t\t\t\twrapperEnd = \"}\";\n\t\t\t\t}\n\t\t\t\tvar mainLoopCode = input.substr(loopMatch.index+loopMatch[0].length, index-loopMatch.index-loopMatch[0].length-1);\n\t\t\t\t// Redefine the \"offset zero\" of our transformed code,\n\t\t\t\t// used to hash methods/properties of contexts provided by shim\n\t\t\t\tinputData.initialDeclarationOffset = wrapperCode.length;\n\t\t\t\toutput = wrapperCode + initCode + finalCode + wrapperEnd + paramsCode + mainLoopCode;\n\t\t\t\t\n\t\t\t\tinputData.interpreterCall = 'setInterval(_,'+delayMatch[1]+')';\n\t\t\t\tinputData.wrappedInit = timeVariableName+'=0';\n\t\t\t\t\n\t\t\t\t// Special case : the assignment of the time variable is done\n\t\t\t\t// as a parameter of setInterval()\n\t\t\t\t// (featured in 2012 - A rose is a rose)\n\t\t\t\tif (delayMatch[1].indexOf(inputData.wrappedInit) != -1) {\n\t\t\t\t\t// in this case, no need to declare the variable again\n\t\t\t\t\tinputData.wrappedInit = \"\";\n\t\t\t\t\tdetails += timeVariableName+\" initialized as parameter to setInterval, kept as is.\\n\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar functionDeclarationOffset = loopMatch.index+loopMatch[0].indexOf(functionDeclaration);\n\t\t\t\t// Record the change in the thermal transform\n\t\t\t\tvar transform = [ { inLength : input.length, outLength : output.length, complete : true } ];\n\t\t\t\t// \"if(!t){\" : mapped to \"function(\"\n\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [functionDeclarationOffset, functionDeclaration.length], rangeOut : [0, wrapperCode.length] });\n\t\t\t\tvar rangeOutBegin = wrapperCode.length;\n\t\t\t\t// code before the main loop, before the time variable declaration, mapped to itself\n\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [0, timeDefinitionBegin], rangeOut : [rangeOutBegin, timeDefinitionBegin] });\n\t\t\t\trangeOutBegin += timeDefinitionBegin;\n\t\t\t\tvar initSecondHalfLength = 0;\n\t\t\t\tif (timeDefinitionMatch) {\n\t\t\t\t\t// code before the main loop, after the time variable declaration, mapped to itself\n\t\t\t\t\t// may omit the final \",\" or \";\", if any, that is eliminated = mapped to nothing\n\t\t\t\t\tinitSecondHalfLength = initCode.length-timeDefinitionBegin;\n\t\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [timeDefinitionEnd, initSecondHalfLength], rangeOut : [rangeOutBegin, initSecondHalfLength] });\n\t\t\t\t\trangeOutBegin += initSecondHalfLength;\n\t\t\t\t}\n\t\t\t\tif (finalCode.length) {\n\t\t\t\t\t// code after the main loop, mapped to itself\n\t\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [index+delayMatch[0].length, finalCode.length], rangeOut : [rangeOutBegin, finalCode.length] });\n\t\t\t\t\trangeOutBegin += finalCode.length;\n\t\t\t\t}\n\t\t\t\t// \"}\" : mapped to final \"}\" of the main loop, if any\n\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [index-1, 1], rangeOut : [rangeOutBegin, wrapperEnd.length] });\n\t\t\t\trangeOutBegin += wrapperEnd.length;\n\t\t\t\t// parameters of the loop function, mapped to themselves\n\t\t\t\tvar paramsOffset = loopMatch.index+loopMatch[0].indexOf(loopMatch[1]);\n\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [paramsOffset, loopMatch[1].length], rangeOut : [rangeOutBegin, paramsCode.length] });\n\t\t\t\trangeOutBegin += paramsCode.length;\n\t\t\t\t// code in the loop function, mapped to itself\n\t\t\t\ttransform.push ( { chapter : 0, rangeIn : [loopMatch.index+loopMatch[0].length, mainLoopCode.length], rangeOut : [rangeOutBegin, mainLoopCode.length] });\n\t\t\t\t// \"setInterval(\" : map to original declaration\n\t\t\t\tvar blockLength = \"setInterval(\".length;\n\t\t\t\ttransform.push ( { chapter : 3, rangeIn : [loopMatch.index, blockLength], rangeOut : [0, blockLength] });\n\t\t\t\t// \"_\" : mapped to \"function(\"\n\t\t\t\ttransform.push ( { chapter : 3, rangeIn : [functionDeclarationOffset, functionDeclaration.length], rangeOut : [blockLength, 1] });\n\t\t\t\t// \",nn)\" : map to original declaration\n\t\t\t\ttransform.push ( { chapter : 3, rangeIn : [index, delayMatch[0].length], rangeOut : [blockLength+1, delayMatch[1].length+2] });\t\t\t\t\n\t\t\t\t// time declaration variable \"t=0\" : map to original declaration if any, to function declaration otherwise\n\t\t\t\tif (timeDefinitionMatch) {\n\t\t\t\t\ttransform.push ( { chapter : 4, rangeIn : [timeDefinitionBegin, timeDefinitionEnd-timeDefinitionBegin], rangeOut : [0, inputData.wrappedInit.length] });\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\ttransform.push ( { chapter : 4, rangeIn : [functionDeclarationOffset, functionDeclaration.length], rangeOut : [0, inputData.wrappedInit.length] });\t\t\t\t\n\t\t\t\t}\n\t\t\t\tinputData.thermalMapping.push(transform);\n\t\t\t\t\n\t\t\t} else {\t// delayMatch === false\n\t\t\t\tdetails += \"Unable to find delay for setInterval, module skipped.\\n\";\n\t\t\t}\n\t\t\t\n\t\t} else {\n\t\t\tdetails += \"setInterval() loop not found, module skipped.\\n\";\n\t\t}\n\t\tdetails += \"\\n\";\n\t\t\n\t\t// output stored in inputData parameter instead of being returned\n\t\tinputData.contents = output;\n\t\tinputData.log += details;\n\t},\t\n\t\n\t/**\n\t * Performs an optimal hashing and renaming of the methods/properties of a canvas 2d context.\n\t * Uses a context reference passed from a shim (if provided), plus attempts to\n\t * identify all contexts created within the code.\n\t * Returns an array containing two sub-arrays, \n\t * each in the same format as the compression methods : \n\t *   [output length, output string, informations],\n\t * even if the preprocessing actually lenghtened the string.\n\t * \n\t *\n\t * @param inputData (constant) PackerData structure containing setup data and the code to preprocess \n\t * @param options (constant) options set, see below for use details\n\t * Options used are :\n\t *  - contextType : type of context provided by shim : 0 for 2D, 1 for GL\n\t *  - contextVariableName : the variable holding the context if provided by shim, false otherwise\n\t *  - varsNotReassigned : boolean array[128], true to avoid altering variable\n\t * @return an array containing branched (and hashed) PackerData, empty if no 2d context definition is found in the code.\n\t */\n\tpreprocess2DContext : function(inputData, options) {\n\t\t// Obtain all context definitions (variable name and location in the code)\n\t\tvar objectNames = [], objectOffsets = [], objectDeclarationLengths = [], searchIndex = 0;\n\t\tvar variableName = (options.contextType==0?options.contextVariableName:false);\n\t\tvar varsNotReassigned = options.varsNotReassigned;\n\t\t// Start with the preset context, if any\n\t\tif (variableName)\n\t\t{\n\t\t\tobjectNames.push(variableName);\n\t\t\tobjectOffsets.push(inputData.initialDeclarationOffset);\n\t\t\tobjectDeclarationLengths.push(0);\n\t\t}\n\t\t// Then search for additional definitions inside the code. Keep name, declaration offset, and declaration length\n\t\tvar input = inputData.contents;\n\t\tvar declarations = input.match (/([\\w\\d.]*)=[\\w\\d.]*\\.getContext\\(?[`'\"]2d[`'\"]\\)?/gi);\n\t\tif (declarations) {\n\t\t\tfor (var declIndex=0; declIndex<declarations.length; ++declIndex)\n\t\t\t{\n\t\t\t\tvar oneDecl = declarations[declIndex];\n\t\t\t\tobjectNames.push(oneDecl.substr(0, oneDecl.indexOf('=')));\n\t\t\t\tvar oneOffset = input.indexOf(oneDecl, searchIndex);\n\t\t\t\tobjectOffsets.push(oneOffset);\n\t\t\t\tobjectDeclarationLengths.push(oneDecl.length);\n\t\t\t\tsearchIndex = oneOffset + oneDecl.length;\n\t\t\t}\n\t\t}\n\n\t\tif (objectNames.length) {\n\t\t\t// obtain the list of properties in a 2D context from the ContextDescriptor\n\t\t\tvar referenceProperties = this.contextDescriptor.canvas2DContextDescription.properties;\n\n\t\t\tvar methodHashedData = PackerData.clone(inputData, \" 2D(methods)\");\n\t\t\tmethodHashedData.log += \"----------- Hashing methods for 2D context -----------\\n\";\n\t\t\t// output stored in methodHashedData\n\t\t\tthis.renameObjectMethods(methodHashedData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned);\n\t\t\t\n\t\t\tvar propertyHashedData = PackerData.clone(inputData, \" 2D(properties)\");\n\t\t\tpropertyHashedData.log += \"----------- Hashing properties for 2D context -----------\\n\";\n\t\t\t// output stored in propertyHashedData\n\t\t\tthis.hashObjectProperties(propertyHashedData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned);\n\t\t\treturn [methodHashedData, propertyHashedData];\n\t\t}\n\t\treturn [];\n\t},\n\n\t/**\n\t * Performs an optimal hashing and renaming of the methods of a canvas WebGL context.\n\t * Uses a context reference passed from a shim, or attempts to locate in inside the code.\n\t * Features the call to replaceWebGLconstants() as one of the results.\n\t * @see replaceWebGLconstants\n\t *\n\t * Returns an array containing three sub-arrays, \n\t *   [output length, output string, informations],\n\t *  - first one has method hashing performed\n\t *  - second one has method hashing + GL constants replaced by their value\n\t *  - third one has method + property renaming\n\t *\n\t * @param inputData (constant) PackerData structure containing setup data and the code to preprocess\n\t * @param options (constant) options set, see below for use details\n\t * Options used are :\n\t *  - contextType : type of context provided by shim : 0 for 2D, 1 for GL\n\t *  - contextVariableName : the variable holding the context if provided by shim, false otherwise\n\t *  - varsNotReassigned : boolean array[128], true to avoid altering variable\n\t * @return an array containing branched (and hashed) PackerData, empty if no WebGL context definition is found in the code.\n\t */\n\tpreprocessWebGLContext : function(inputData, options) {\n\t\t// Obtain all context definitions (variable name and location in the code)\n\t\tvar objectNames = [], objectOffsets = [], objectDeclarationLengths = [], searchIndex = 0;\n\t\tvar input = inputData.contents;\n\t\tvar variableName = options.contextType==1?options.contextVariableName:false;\n\t\tvar varsNotReassigned = options.varsNotReassigned;\n\t\t// Start with the preset context, if any\n\t\tif (variableName)\n\t\t{\n\t\t\tobjectNames.push(variableName);\n\t\t\tobjectOffsets.push(inputData.initialDeclarationOffset);\n\t\t\tobjectDeclarationLengths.push(0);\n\t\t}\n\t\t// Then search for additional definitions inside the code. Keep name, declaration offset, and declaration length\n\t\tvar declarations = input.match (/([\\w\\d.]*)\\s*=\\s*[\\w\\d.]*\\.getContext\\(?[`'\"](experimental-)*webgl[`'\"](,[\\w\\d\\s{}:.,!]*)*\\)?(\\s*\\|\\|\\s*[\\w\\d.]*\\.getContext\\(?[`'\"](experimental-)*webgl[`'\"](,[\\w\\d\\s{}:.,!]*)*\\)?)*/gi);\n\t\tif (declarations) {\n\t\t\tfor (var declIndex=0; declIndex<declarations.length; ++declIndex)\n\t\t\t{\n\t\t\t\tvar oneDecl = declarations[declIndex];\n\t\t\t\tobjectNames.push(oneDecl.substr(0, oneDecl.indexOf('=')));\n\t\t\t\tvar oneOffset = input.indexOf(oneDecl, searchIndex);\n\t\t\t\tobjectOffsets.push(oneOffset);\n\t\t\t\tobjectDeclarationLengths.push(oneDecl.length);\n\t\t\t\tsearchIndex = oneOffset + oneDecl.length;\n\t\t\t}\n\t\t}\n\t\t\t\n\t\tif (objectNames.length) {\n\t\t\t// list of properties in a GL context\n\t\t\tvar referenceProperties = this.contextDescriptor.canvasGLContextDescription.properties;\n\t\n\t\t\tvar methodHashedData = PackerData.clone(inputData, \" WebGL(methods)\");\n\t\t\tmethodHashedData.log += \"----------- Hashing methods for GL context -----------\\n\";\n\t\t\t// output stored in methodHashedData\n\t\t\tthis.renameObjectMethods(methodHashedData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned);\n\n\t\t\t// elaborate on the previous result : replace GL constants with values\n\t\t\tvar constantHashedData = PackerData.clone(methodHashedData, \" WebGL(methods+constants)\");\n\t\t\tconstantHashedData.log += \"----------- Replacing constants for GL context -----------\\n\";\n\t\t\t// output stored in constantHashedData\n\t\t\tthis.replaceWebGLconstants(constantHashedData, objectNames, this.contextDescriptor.canvasGLContextDescription.constants);\n\n\t\t\tvar propertyHashedData = PackerData.clone(inputData, \" WebGL(properties)\");\n\t\t\tpropertyHashedData.log += \"----------- Hashing properties for GL context -----------\\n\";\n\t\t\t// output stored in propertyHashedData\n\t\t\tthis.hashObjectProperties(propertyHashedData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned);\n\t\n\t\t\treturn [methodHashedData, constantHashedData, propertyHashedData];\n\t\t}\n\n\t\treturn [];\n\t},\n\t\n\t/**\n\t * Replaces, in the provided code, all definition of WebGL constants with their numerical values\n\t * Uses a context reference passed from a shim, or attempts to locate in inside the code.\n\t * Returns an array in the same format as the compression methods : [output length, output string, informations],\n\t * even if the replacement actually lenghtened the string.\n\t * Only the constant values in CAPITALS are replaced. Other properties and methods are untouched.\n\t *\n\t * @param inputData (in/out) PackerData structure containing setup data and the code to preprocess\n\t * @param objectNames array containing variable names of context objects (mandatory)\n\t * @param referenceConstants : object containing all constants of the GL context\n\t * @return nothing - output is stored in parameter inputData\n\t */\n\treplaceWebGLconstants : function (inputData, objectNames, referenceConstants) {\n\t\tvar output = inputData.contents, details=inputData.log;\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tvar exp = new RegExp(\"(^|[^\\\\w$])\"+objectNames[contextIndex]+\"\\\\.([0-9A-Z_]*)[^\\\\w\\\\d_(]\",\"g\");\n\t\t\tvar constantsInUse=[];\n\t\t\tvar result=exp.exec(output);\n\t\t\twhile (result) {\t// get a set with a unique entry for each method\n\t\t\t\tif (constantsInUse.indexOf(result[2])==-1) {\n\t\t\t\t\tconstantsInUse.push(result[2]);\n\t\t\t\t}\n\t\t\t\tresult=exp.exec(output);\n\t\t\t}\n\n\t\t\tdetails += \"Replaced constants of \"+objectNames[contextIndex]+\"\\n\";\n\t\t\tfor (var index=0; index<constantsInUse.length; ++index) {\n\t\t\t\tif (constantsInUse[index] in referenceConstants) {\n\t\t\t\t\tvar constant = referenceConstants[constantsInUse[index]];\n\t\t\t\t\tvar exp = new RegExp(\"(^|[^\\\\w$])\"+objectNames[contextIndex]+\"\\\\.\"+constantsInUse[index]+\"(^|[^\\\\w$])\",\"g\");\t\t\t\t\t\t\n\t\t\t\t\t// output = output.replace(exp, \"$1\"+constant+\"$2\");\n\t\t\t\t\toutput = this.stringHelper.matchAndReplaceAll(output, exp, objectNames[contextIndex]+\".\"+constantsInUse[index], constant, \"\", \"\", 0, inputData.thermalMapping);\n\t\t\t\t\t// show the renaming in the details\n\t\t\t\t\tdetails += constantsInUse[index] + \" -> \" + constant + \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdetails+=\"\\n\";\n\t\t\n\t\t// output stored in inputData parameter\n\t\tinputData.contents = output;\n\t\tinputData.log = details;\n\t},\n\t\n\t/**\n\t * Performs an optimal hashing and renaming of the methods of an AudioContext.\n\t * Unlike 2D contexts, only one audio context is considered.\n\t *\n\t * Returns an array in the same format as the compression methods : [output length, output string, informations],\n\t * even if the preprocessing actually lenghtened the string.\n\t *\n\t * @param inputData (constant) PackerData structure containing the code to refactor and setup \n\t * @param varsNotReassigned boolean array[128], true to keep name of variable\n\t * @return an array containing branched (and hashed) PackerData, empty if no AudioContext definition is found in the code.\n\t */\n\tpreprocessAudioContext : function(inputData, varsNotReassigned) {\n\t\t// list of properties in an AudioContext\n\t\tvar referenceProperties = this.contextDescriptor.audioContextDescription.properties;\n\n\t\t// Initialization of a *AudioContext can be performed in a variety of ways\n\t\t// and is usually less consistent that a 2D or WebGL graphic context.\n\t\t// Multiple tests cover the most common cases\n\t\tvar objectOffset = 0;\n\t\tvar replacementOffset = 0;\n\t\tvar objectName = \"\";\n\t\tvar methodHashedData = PackerData.clone(inputData, \" Audio\");\n\t\tvar input = methodHashedData.contents;\n\t\tmethodHashedData.log += \"----------- Hashing methods for AudioContext --------------------\\n\";\n\n\t\t// direct instanciation of an AudioContext\n\t\t// var c = new AudioContext()\n\t\tvar result = input.match (/([\\w\\.]*)\\s*=\\s*new AudioContext/i);\n\t\tif (result) {\n\t\t\tobjectOffset = input.indexOf(result[0]);\n\t\t\tobjectName = result[1];\n\t\t\t// start replacement at the next semicolon\n\t\t\treplacementOffset = 1+input.indexOf(\";\", objectOffset);\n\t\t\t// unless the object is used before\n\t\t\treplacementOffset = Math.min(replacementOffset, input.indexOf(objectName, objectOffset+result[0].length));\n\t\t}\n\t\t// direct instanciation of a webkitAudioContext\n\t\t// beware, the same code could try to create both (see TestCase audioContext_create1)\n\t\t// var c = new webkitAudioContext()\n\t\tvar resultWebkit = input.match (/([\\w\\.]*)\\s*=\\s*new webkitAudioContext/i);\n\t\tif (resultWebkit) {\n\t\t\t\n\t\t\tif (resultWebkit[1] == objectName || objectName == \"\") {\n\t\t\t\t// we take the latter declaration, as to add the renaming loop after both of them\n\t\t\t\tobjectOffset = Math.max(objectOffset, input.indexOf(resultWebkit[0]));\n\t\t\t\t// start replacement at the next semicolon\n\t\t\t\treplacementOffset = 1+input.indexOf(\";\", objectOffset);\n\t\t\t\t// unless the object is used before\n\t\t\t\treplacementOffset = Math.min(replacementOffset, input.indexOf(objectName, objectOffset+resultWebkit[0].length));\n\t\t\t} else {\n\t\t\t\t// the webkitAudioContext was created with a different name than the AudioContext\n\t\t\t\t// in this case, we separately rename objects for both of them \n\t\t\t\t// (see TestCase audioContext_create2 and audioContext_create3)\n\t\t\t\tvar webkitObjectOffset = input.indexOf(resultWebkit[0]);\n\t\t\t\tvar webkitObjectName = resultWebkit[1];\n\t\t\t\t// start replacement at the next semicolon\n\t\t\t\tvar webkitReplacementOffset = 1+input.indexOf(\";\", webkitObjectOffset);\n\t\t\t\t// unless the object is used before\n\t\t\t\twebkitReplacementOffset = Math.min(webkitReplacementOffset, input.indexOf(webkitObjectName, webkitObjectOffset+resultWebkit[0].length));\n\n\t\t\t\t\n\t\t\t\tvar secondObjectOffset = replacementOffset>webkitReplacementOffset ? objectOffset : webkitObjectOffset;\n\t\t\t\tvar secondReplacementOffset = replacementOffset>webkitReplacementOffset ? replacementOffset : webkitReplacementOffset;\n\t\t\t\tvar secondObjectName = replacementOffset>webkitReplacementOffset ? objectName : resultWebkit[1];\n\t\t\t\t\n\t\t\t\tobjectOffset = replacementOffset>webkitReplacementOffset ? webkitObjectOffset : objectOffset ;\n\t\t\t\treplacementOffset = replacementOffset>webkitReplacementOffset ? webkitReplacementOffset : replacementOffset ;\n\t\t\t\tobjectName = replacementOffset>webkitReplacementOffset ? resultWebkit[1] : objectName ;\n\t\t\t\t\n\t\t\t\t// perform the replacement for the latter object first, so the offset of the former is not changed\n\t\t\t\tthis.renameObjectMethods(methodHashedData, [secondObjectName], [secondReplacementOffset], [0], referenceProperties, varsNotReassigned);\n\t\t\t}\n\t\t}\n\t\t// direct instanciation of the appropriate context\n\t\t// var c = new (window.AudioContext||window.webkitAudioContext)()\n\t\tresult = input.match (/([\\w\\.]*)\\s*=\\s*new\\s*\\(*\\s*(window\\.)*(webkit)*AudioContext\\s*\\|\\|\\s*(window\\.)*(webkit)*AudioContext/i);\n\t\tif (result) {\n\t\t\tobjectOffset = input.indexOf(result[0]);\n\t\t\tobjectName = result[1];\n\t\t\t// start replacement at the next semicolon\n\t\t\treplacementOffset = 1+input.indexOf(\";\", objectOffset);\n\t\t\t// unless the object is used before\n\t\t\treplacementOffset = Math.min(replacementOffset, input.indexOf(objectName, objectOffset+result[0].length));\n\t\t}\n\t\t\n\t\t// direct instanciation of the appropriate context, not factored in\n\t\t// var c = new AudioContext()||new webkitAudioContext()\n\t\t// (see TestCase audioContext_create4)\n\t\tresult = input.match (/([\\w\\.]*)\\s*=\\s*new\\s*\\(*\\s*(window\\.)*(webkit)*AudioContext(\\(\\))*\\s*\\|\\|\\s*new\\s*(window\\.)*(webkit)*AudioContext(\\(\\))*/i);\n\t\tif (result) {\n\t\t\tobjectOffset = input.indexOf(result[0]);\n\t\t\tobjectName = result[1];\n\t\t\t// start replacement at the next semicolon\n\t\t\treplacementOffset = 1+input.indexOf(\";\", objectOffset);\n\t\t\t// unless the object is used before\n\t\t\treplacementOffset = Math.min(replacementOffset, input.indexOf(objectName, objectOffset+result[0].length));\n\t\t}\n\t\t\n\t\t// instanciation of the appropriate context, through a temporary variable\n\t\t// contextType = window.AudioContext||window.webkitAudioContext; var c = new contextType;\n\t\tresult = input.match (/([\\w\\.]*)\\s*=\\s*\\(*\\s*(window\\.)*(webkit)*AudioContext\\s*\\|\\|\\s*(window\\.)*(webkit)*AudioContext/i);\n\t\tif (result) {\n\t\t\tvar contextTypeName = result[1];\n\t\t\tvar exp = new RegExp(\"([\\\\w\\\\.]*)\\\\s*=\\\\s*new\\\\s*\\\\(*\\\\s*\"+contextTypeName);\n\t\t\tresult = input.match(exp);\n\t\t\tif (result) {\n\t\t\t\tobjectOffset = input.indexOf(result[0]);\n\t\t\t\tobjectName = result[1];\n\t\t\t\t// start replacement at the next semicolon\n\t\t\t\treplacementOffset = 1+input.indexOf(\";\", objectOffset);\n\t\t\t\t// unless the object is used before\n\t\t\t\treplacementOffset = Math.min(replacementOffset, input.indexOf(objectName, objectOffset+result[0].length));\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (replacementOffset>0) {\n\t\t\tthis.renameObjectMethods(methodHashedData, [objectName], [replacementOffset], [0], referenceProperties, varsNotReassigned);\n\t\t\treturn [methodHashedData];\n\t\t}\n\t\treturn [];\n\t},\n\t\n\t/**\n\t * Implementation of #86\n\t * Identifies method and object properties names used multiple times\n\t * Stores their name as a variable on the first occurrence, then use the variable as a property afterwards\n\t *\n\t * c.fillStyle becomes c[F=\"fillStyle\"] the first time then c[F] afterwards.\n\t *\n\t * The advantage over hashing schemes is that this works with different objects sharing the same property\n\t *\n\t * Variables assigned that way are from the same pool that would be used for reassignment,\n\t * but leaving enough for that module to complete.\n\t *\n\t * Returns an array [boolean, PackerData]\n\t *  - the boolean is true if at least one property name was packed that way, false if no change occurred\n\t *  - the PackerData derives from the input with the packed properties \n\t *\n\t * @param inputData (constant) PackerData structure containing the code to refactor and setup \n\t * @param options : preprocessing options, as follows\n\t *       -  reassignVars : true to globally reassign variable names \n\t *       -  varsNotReassigned : string or array listing all protected variables (whose name will not be modified)\n\t *       -  wrapInSetInterval : true to wrap the unpacked code in a setInterval() call instead of eval()\n\t *       -  timeVariableName : if \"setInterval\" option is set, the variable to use for time (zero on first loop, nonzero after)\n\t * @return an array containing [boolean, PackerData] as described above\n\t */\n\thashPropertyNamesAsVariables : function (inputData, options) {\n\t\tvar outputData = PackerData.clone(inputData, \" all_properties\");\n\t\tvar input = outputData.contents;\n\n\t\t// Anything inside strings (save for template literals) is ignored.\n\t\t// Strings boundaries will be identified again after the preprocessing is done\n\t\tthis.identifyStrings(outputData, true); \n\t\t\n\t\tvar propertiesInUse=[];\n\t\tvar propertiesMatchData=[];\n\t\tvar exp = new RegExp(\"\\\\.(\\\\w*)\\\\W\",\"g\");\n\t\tvar result=exp.exec(input);\n\t\twhile (result) {\t// get a set with a unique entry for each method\n\t\t\tvar recordIndex = propertiesInUse.indexOf(result[1]);\n\t\t\tif (this.stringHelper.isActualCodeAt(recordIndex, outputData)) {\n\t\t\t\tif (recordIndex == -1) { \n\t\t\t\t\tpropertiesInUse.push(result[1]);\t\n\t\t\t\t\tpropertiesMatchData.push({name:result[1], count:1, offset:exp.lastIndex});\n\t\t\t\t} else {\n\t\t\t\t\t++propertiesMatchData[recordIndex].count;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult=exp.exec(input);\n\t\t}\n\t\n\t\t\t\n\t\tfor (var index=0; index<propertiesMatchData.length; ++index) {\n\t\t\tvar rawCost = (1+propertiesMatchData[index].name.length) * propertiesMatchData[index].count;\n\t\t\tvar hashedCost = propertiesMatchData[index].name.length + 6 + (propertiesMatchData[index].count-1)*3;\n\t\t\tpropertiesMatchData[index].gain = rawCost - hashedCost;\n\t\t\n\t\t}\n\t\n\t\t// sort by decreasing gain, offset as tiebreaker to ensure a deterministic result\n\t\t// even when there are equivalent gains\n\t\tpropertiesMatchData.sort(function(a,b) { return b.gain-a.gain + (b.offset-a.offset)/10000; });\n\t\t\n\t\toutputData.log += \"----------- Storing property names into variables ----------- \\n\";\n\t\t\n\t\tvar keywordsAndVariables = this.discriminateKeywordsAndVariables(inputData, options);\n\t\tvar keywordChars = keywordsAndVariables[0];\n\t\tvar variableChars = keywordsAndVariables[1];\n\t\tvar availableChars = keywordsAndVariables[2];\n\t\tvar variableCharsOnly = keywordsAndVariables[3];\n\t\t\n\t\tvar availableCharList = \"\";\n\t\tvar charsNeededForReassignModule = 0;\n\t\tvar formerVariableCharList = \"\";\n\t\tfor (var i=32; i<128; ++i) {\n\t\t\t// Identify as available all characters used in keywords but not variables\n\t\t\tif (availableChars[i]) {\n\t\t\t\tavailableCharList+=String.fromCharCode(i);\n\t\t\t}\n\t\t\t// Simply count variables that need to be renamed, if the \"reassign variables\" module is active\n\t\t\t// They shall be deducted from our pool\n\t\t\tif (variableCharsOnly[i] && options.reassignVars) {\n\t\t\t\t++charsNeededForReassignModule;\n\t\t\t}\n\t\t}\n\t\tvar availableVariableCount = availableCharList.length - charsNeededForReassignModule;\n\t\tvar processedInput = outputData.contents;\n\t\tfor (var index=0 ; index<propertiesMatchData.length && propertiesMatchData[index].gain>0 && index<availableVariableCount ; ++index) {\n\t\t\n\t\t\t// on the first occurrence, declare the variable\n\t\t\tvar declarationReplacement = '['+availableCharList[index]+'=\"'+propertiesMatchData[index].name+'\"]';\n\t\t\t// replace all other occurences without the declaration\n\t\t\tvar otherReplacement = '['+availableCharList[index]+']';\n\t\t\tprocessedInput = this.stringHelper.matchAndReplaceFirstAndAll(processedInput, false, '.'+propertiesMatchData[index].name, declarationReplacement, otherReplacement, \"\", \"\", 0, outputData.thermalMapping);\n\t\t\t\n\t\t\toutputData.log += availableCharList[index] + '=\"' + propertiesMatchData[index].name+ '\" (x'+propertiesMatchData[index].count + ') : gain = '+propertiesMatchData[index].gain+\"\\n\";\t\t\n\t\t}\n\t\t\n\t\t// log the replacements missed for lack of a token\n\t\tfor ( ; index<propertiesMatchData.length && propertiesMatchData[index].gain>0 ; ++index) {\n\t\t\toutputData.log += 'No token left : \"' + propertiesMatchData[index].name+ '\" (x'+propertiesMatchData[index].count + ') : missed gain = '+propertiesMatchData[index].gain+\"\\n\";\t\t\n\t\t}\n\t\t\t\t\n\t\toutputData.contents = processedInput; \n\t\treturn [index>0, outputData];\n\t},\n\t\n\t\n\t/**\n\t * Identifies the optimal hashing function (the one returning the shortest result)\n\t * then renames all the methods with their respective hash, and preprends the hashing code.\n\t *\n\t * The hashing loop looks like : for(i in c)c[i[0]+[i[6]]=c[i];\n\t * meaning that later one may call c.fc(...) instead of c.fillRect(...)\n\t * The newly created properties are reference to existing functions.\n\t *\n\t * Replacement is performed at the last object assignment(graphic or audio context), \n\t * or at the beginning for shim context, hence the offset parameter.\n\t *\n\t * If there are several contexts, only one hash is used. It is applied to\n\t * all or only some of the contexts, depending on the computed gain.\n\t * The algorithm will not define different hashes for the multiple\n\t * contexts. The rationale behind this is the assumption that the lesser\n\t * gain from using the same hash for all will be offset by the better\n\t * compression - as the repeated hashing pattern will be picked up by the\n\t * packer.\n\t *\n \t * Returns an array in the same format as the compression methods : [output length, output string, informations],\n\t *\n\t * @param inputData (in/out) PackerData structure containing the code to refactor and setup \n\t * @param objectNames : array containing variable names of context objects, whose methods to rename in the source string\n\t * @param objectOffsets : array, in the same order, of character offset to the beginning of the object declaration. Zero if defined outside (shim)\n\t * @param objectDeclarationLengths : array, in the same order, of lengths of the object declaration, starting at offset. Zero if defined outside (shim)\n\t * @param referenceProperties : an array of strings containing property names for the appropriate context type\n\t * @param varsNotReassigned : boolean array[128], true to keep name of variable\n\t * @return true if the replacement was performed (the gain was >= 0), false otherwise (net loss, replacement cancelled)\n\t */\n\trenameObjectMethods : function(inputData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned)\n\t{\n\t\tvar input = inputData.contents;\n\t\tvar details = inputData.log;\n\t\tvar methodsInUseByContext=[];\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t{\n\t\t\tvar methodsInUse = [];\n\t\t\tvar exp = new RegExp(\"(^|[^\\\\w$])\"+objectNames[contextIndex]+\"\\\\.(\\\\w*)\\\\(\",\"g\");\n\t\t\tvar result=exp.exec(input);\n\t\t\twhile (result) {\t// get a set with a unique entry for each method\n\t\t\t\tif (methodsInUse.indexOf(result[2])==-1) { \n\t\t\t\t\tmethodsInUse.push(result[2]);\t\n\t\t\t\t}\n\t\t\t\t--exp.lastIndex; // the final ( can be reused as initial separator in the next expression\n\t\t\t\tresult=exp.exec(input);\n\t\t\t}\n\t\t\tmethodsInUseByContext.push(methodsInUse);\n\t\t}\n\n\t\t\n\t\tvar bestTotalScore = -999, bestIndex =-1, bestXValue = 0, bestYValue = 0, bestScoreByContext = [];\n\t\t// For each hashing function, we compute the hashed names of all methods of the context object\n\t\t// All collisions (such as taking the first letter of scale() and save() end up in s()) are eliminated\n\t\t// as the order depends on the browser and we cannot assume the browser that will be running the final code\n\t\t// A score is then assigned to the hashing function :\n\t\t//   - for each context, the algorithm computes the gain obtained by shortening the non-colliding method names to their hash\n\t\t//     (the number of occurrences is irrelevant, we assume that the compression step will amount them to one)\n\t\t//   - the length of the hashing function is subtracted\n\t\t// Only the hashing function with the best score is kept - and applied\n\t\tfor (var functionIndex=0; functionIndex<this.hashFunctions.length; ++functionIndex) {\n\t\t\tvar functionDesc = this.hashFunctions[functionIndex];\n\t\t\tfor (var xValue=functionDesc[1]; xValue<=functionDesc[2] ; ++xValue) {\n\t\t\t\tfor (var yValue=functionDesc[3]; yValue<=functionDesc[4] ; ++yValue) {\n\t\t\t\t\tvar reverseLookup = [], forwardLookup = [];\n\t\t\t\t\tfor (var index=0; index<referenceProperties.length; ++index) {\n\t\t\t\t\t\tvar w = referenceProperties[index];\n\t\t\t\t\t\tvar hashedName = functionDesc[5].call(null, w, xValue, yValue);\n\t\t\t\t\t\treverseLookup[hashedName] = (reverseLookup[hashedName] ? \"<collision>\" : w);\n\t\t\t\t\t}\n\t\t\t\t\tfor (w in reverseLookup) {\n\t\t\t\t\t\tforwardLookup[reverseLookup[w]]=w;\t// keep only the method names with no collisions\n\t\t\t\t\t}\n\t\t\t\t\tvar allScores = [], totalScore = 0;\n\t\t\t\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t\t\t\t{\n\t\t\t\t\t\tvar score = 0;\n\t\t\t\t\t\tvar methodsInUse = methodsInUseByContext[contextIndex];\n\t\t\t\t\t\tfor (var methodIndex=0; methodIndex<methodsInUse.length; ++methodIndex) {\n\t\t\t\t\t\t\t// Fix for issue #11 : in FF, arrays have a method fill(), as 2D contexts do\n\t\t\t\t\t\t\t// typeof() discriminates between \"string\" (hash match), \"undefined\" (no match) and \"function\" (array built-in)\n\t\t\t\t\t\t\tif (typeof(forwardLookup[methodsInUse[methodIndex]])==\"string\") {\n\t\t\t\t\t\t\t\tvar delta = methodsInUse[methodIndex].length - forwardLookup[methodsInUse[methodIndex]].length;\n\t\t\t\t\t\t\t\t// Complement for issue #23, when the hash could be longer than the original name\n\t\t\t\t\t\t\t\tscore += Math.max(0, delta);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tscore -= 2; // a[hash]=b[hash]=a[i], \"[hash]=\" is packed for 1, remains \"a\" total 1+1\n\t\t\t\t\t\tallScores.push(score); \n\t\t\t\t\t\tscore = Math.max(0, score); // if the gain is negative, no replacement will be performed\n\t\t\t\t\t\ttotalScore += score;\n\t\t\t\t\t}\n\t\t\t\t\t// the score for the hash is the gain as computed above,\n\t\t\t\t\t// minus the length of the hash function itself.\n\t\t\t\t\ttotalScore-=functionDesc[0].replace(/x/g, xValue).replace(/y/g, yValue).length;\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tif (totalScore>bestTotalScore) {\n\t\t\t\t\t\tbestTotalScore = totalScore;\n\t\t\t\t\t\tbestScoreByContext = allScores;\n\t\t\t\t\t\tbestIndex = functionIndex;\n\t\t\t\t\t\tbestXValue = xValue;\n\t\t\t\t\t\tbestYValue = yValue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// bail out early if no gain. Keep if just par, to see how compression behaves\n\t\tif (bestTotalScore < 0) {\n\t\t\tdetails += \"Best hash loses \"+(-bestTotalScore)+\" bytes, skipping.\\n\";\n\t\t\tinputData.log = details;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// best hash function (based on byte gain) found. Apply it\n\t\tvar reverseLookup = [], forwardLookup = [];\n\t\tfor (var index=0; index<referenceProperties.length; ++index) {\n\t\t\tvar w = referenceProperties[index];\n\t\t\tvar hashedName = this.hashFunctions[bestIndex][5].call(null, w, bestXValue, bestYValue);\n\t\t\treverseLookup[hashedName] = (reverseLookup[hashedName] ? \"<collision>\" : w);\n\t\t}\n\t\tfor (w in reverseLookup) {\n\t\t\tforwardLookup[reverseLookup[w]]=w;\n\t\t}\n\t\tvar hashedCode = input;\n\t\t\n\t\t// Tell the user what is being replaced, and what is ignored\n\t\tvar renamedList = \"\", notRenamedList = \"\";\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t{\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\n\t\t\t\trenamedList += (renamedList.length>0?\", \" : \"\") + objectNames[contextIndex];\n\t\t\t} else {\n\t\t\t\tnotRenamedList += (notRenamedList.length>0?\", \" : \"\") + objectNames[contextIndex];\n\t\t\t}\n\t\t}\n\t\tif (notRenamedList.length>0) {\n\t\t\tdetails += \"No renaming for \"+notRenamedList+\"\\n\";\n\t\t}\n\t\tif (renamedList.length>0) {\n\t\t\tdetails += \"Renamed methods for \"+renamedList+\"\\n\";\n\t\t}\n\t\t\n\t\t// Perform the replacement inside the code\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\t// replace only if the gain is positive\n\t\t\t\tvar methodsInUse = methodsInUseByContext[contextIndex];\n\t\t\t\tfor (var methodIndex=0; methodIndex<methodsInUse.length; ++methodIndex) {\n\t\t\t\t\n\t\t\t\t\t// Fix for issue #11, needed in this iteration again\n\t\t\t\t\tif (typeof(forwardLookup[methodsInUse[methodIndex]])==\"string\") {\n\t\t\t\t\t\tvar gain = methodsInUse[methodIndex].length - forwardLookup[methodsInUse[methodIndex]].length;\n\t\t\t\t\t\tif (gain > 0) {\n\t\t\t\t\t\t\t// skip replacement if the hash would be longer than the original method\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t// otherwise replace all instances of that method (\"g\" option in the RegExp)\n\t\t\t\t\t\t\t// The opening bracket at the end avoids triggering on a subset of the name,\n\t\t\t\t\t\t\t// for instance enable() instead of enableVertexAttribArray() in WebGL contexts\n\t\t\t\t\t\t\t// The initial \\b avoids triggering if one context has a name\n\t\t\t\t\t\t\t// ending in another context's name (such as 'c' and 'cc')\n\t\t\t\t\t\t\tvar exp = new RegExp(\"\\\\b\"+objectNames[contextIndex]+\"\\\\.\"+methodsInUse[methodIndex]+\"\\\\(\",\"g\");\t\t\t\t\t\t\n\t\t\t\t\t\t\t//hashedCode = hashedCode.replace(exp, objectNames[contextIndex]+\".\"+forwardLookup[methodsInUse[methodIndex]]+\"(\");\n\t\t\t\t\t\t\thashedCode = this.stringHelper.matchAndReplaceAll(hashedCode, exp, methodsInUse[methodIndex], forwardLookup[methodsInUse[methodIndex]], \"\", \"\", 0, inputData.thermalMapping);\n\n\t\t\t\t\t\t\t// show the renaming in the details, for used methods only\n\t\t\t\t\t\t\tdetails += objectNames[contextIndex]+\".\"+forwardLookup[methodsInUse[methodIndex]] + \" -> \" + methodsInUse[methodIndex] + \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdetails += \"\\n\";\n\t\t\n\t\t// Choose the index variable for the hashing loop\n\t\tvar loopVarResult = this.getMostFrequentLoopVariable(input, varsNotReassigned);\n\t\tvar indexName = loopVarResult[0];\n\t\tdetails += loopVarResult[1]; // operation log\t\t\n\n\t\t// Create the final hashing expression by replacing the placeholder variables\n\t\tvar expression = this.hashFunctions[bestIndex][0].replace(/w/g, indexName);\n\t\texpression = expression.replace(/x/g, bestXValue);\t\t\n\t\texpression = expression.replace(/y/g, bestYValue);\t\t\n\t\t// If the input code uses mostly \"\" as string delimiters, use it as well in the expression instead of ''\n\t\tif (input.split('\"').length>input.split(\"'\").length) {\n\t\t\texpression = expression.replace(/'/g, '\"');\n\t\t}\n\n\t\t// Determine where in the code the hashing loop will be inserted.\n\t\t// (at the declaration of the last context that gets hashed).\n\t\tvar offset = 0, loopContext = 0, shortestContext = 0;\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\t// replace only if the gain is positive\n\t\t\t\toffset = objectOffsets[contextIndex];\t// perform the replacement at the latest context definition\n\t\t\t\tloopContext = contextIndex;\n\t\t\t\t// retrieve the context with the shortest name, will be used\n\t\t\t\t// as right-member of the hashing assignment\n\t\t\t\tif (objectNames[contextIndex].length <  objectNames[shortestContext].length) {\n\t\t\t\t\tshortestContext = contextIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvar outputIntro = hashedCode.substr(0, offset);\n\t\t\n\t\t// Insert the hashing/renaming loop in the code.\n\t\t// If the context definition is not included (js1k shim for instance), the loop is prepended to the code and ends with \";\"\n\t\t// otherwise the loop replaces and includes the last declaration. The code looks like :\n\t\t//   for (i in c=a.getContext('2d'))c[...]=c[i] with a single context\n\t\t//   for (i in c=a.getContext('2d'))c[...]=b[...]=b[i] with multiple contexts (surprising, but it works)\n\t\t// The ending separator is kept, unless it is a comma \",\", in which case it is replaced with a semicolon \";\"\n\t\t// (to avoid including the trailing code in the loop)\n\t\tvar declarationLength = objectDeclarationLengths[loopContext];\n\t\tvar outputInitBlock1 = \"for(\"+indexName+\" in \"+(declarationLength==0?objectNames[loopContext]:\"\");\n\t\tvar outputInitBlock2 = (declarationLength==0?\"\":hashedCode.substr(offset, declarationLength));\n\t\tvar outputInitBlock3 = \")\";\n\t\t//output+=\"for(\"+indexName+\" in \"+(declarationLength==0?objectNames[loopContext]:hashedCode.substr(offset, declarationLength))+\")\";\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\t\n\t\t\t\toutputInitBlock3+=objectNames[contextIndex]+\"[\"+expression+\"]=\";\n\t\t\t}\n\t\t}\t\t\n\t\toutputInitBlock3+=objectNames[shortestContext]+\"[\"+indexName+\"]\";\n\t\tif (hashedCode[offset+declarationLength]==\",\") {\n\t\t\t// replace the trailing \",\" with \";\" as explained above\n\t\t\toutputInitBlock3+=\";\";\n\t\t\t++declarationLength;\n\t\t}\n\t\toutputInitBlock3+=(declarationLength==0?\";\":\"\");\n\t\tvar outputMain = hashedCode.substr(offset+declarationLength);\n\t\tvar output = outputIntro + outputInitBlock1 + outputInitBlock2 + outputInitBlock3 + outputMain;\n\t\t\n\t\t// Record the renaming loop in the thermal transform\n\t\tvar transform = [ { inLength : hashedCode.length, outLength : output.length, complete : true } ];\n\t\tif (offset) {\n\t\t\t// code before the context declaration and renaming loop, kept unchanged\n\t\t\ttransform.push ( { chapter : 0, rangeIn : [0, offset], rangeOut : [0, offset] });\n\t\t}\n\t\tvar rangeOutBegin = offset;\n\t\tif (declarationLength) {\t// context declaration done inside the code\n\t\t\t// \"for (i in \" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [offset, outputInitBlock1.length] });\n\t\t\trangeOutBegin += outputInitBlock1.length;\n\t\t\t// \"context=canvas.getContext('2d')\" : map as is\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset, outputInitBlock2.length],\n\t\t\t\t\t\t\t   rangeOut : [rangeOutBegin, outputInitBlock2.length] });\n\t\t\trangeOutBegin += outputInitBlock2.length;\n\t\t\t// \")c[...]=c[i]\" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [rangeOutBegin, outputInitBlock3.length] });\n\t\t\trangeOutBegin += outputInitBlock3.length;\n\t\t\n\t\t} else {\t// context declaration done beforehand (in the shim)\n\t\t\t// \"for (i in c)c[...]=c[i];\" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [offset, outputInitBlock1.length+outputInitBlock2.length+outputInitBlock3.length] });\n\t\t\trangeOutBegin += outputInitBlock1.length+outputInitBlock2.length+outputInitBlock3.length;\n\t\t}\n\t\t// code using the hashed contexts\n\t\ttransform.push( { \tchapter : 0, \n\t\t\t\t\t\t\trangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\trangeOut : [rangeOutBegin, outputMain.length] });\n\t\t\n\t\tinputData.thermalMapping.push(transform);\n\t\t\n\t\t\n\t\t// output stored in inputData parameter\n\t\tinputData.contents = output;\n\t\tinputData.log = details;\n\t\treturn true;\n\t},\n\n\t\n\t/**\n\t * Identifies the optimal hashing function (the one returning the shortest result)\n\t * then renames all the properties with their respective hash, and preprends the hashing code.\n\t *\n\t * The hashing loop looks like : for(i in c)c[i[0]+[i[6]]=i;\n\t * The new properties in c contain the full name of actual properties and methods\n\t * meaning that later one may call c[c.fc](...) instead of c.fillRect(...)\n\t * or c[c.fy] instead of c.fillStyle\n\t * Unlike renameObjectMethods(), this works on properties and methods alike.\n\t *\n\t * Replacement is performed at the last object assignment(graphic or audio context), \n\t * or at the beginning for shim context, hence the offset parameter.\n\t *\n\t * If there are several contexts, only one hash is used. It is applied to\n\t * all or only some of the contexts, depending on the computed gain.\n\t * The algorithm will not define different hashes for the multiple\n\t * contexts. The rationale behind this is the assumption that the lesser\n\t * gain from using the same hash for all will be offset by the better\n\t * compression - as the repeated hashing pattern will be picked up by the\n\t * packer.\n\t *\n \t * Returns an array in the same format as the compression methods : [output length, output string, informations],\n\t *\n\t * @param inputData (in/out) PackerData structure containing the code to refactor and setup \n\t * @param objectNames : array containing variable names of context objects, whose methods to rename in the source string\n\t * @param objectOffsets : array, in the same order, of character offset to the beginning of the object declaration. Zero if defined outside (shim)\n\t * @param objectDeclarationLengths : array, in the same order, of lengths of the object declaration, starting at offset. Zero if defined outside (shim)\n\t * @param referenceProperties : an array of strings containing property names for the appropriate context type\n\t * @param varsNotReassigned : boolean array[128], true to keep name of variable\n\t * @return the result of the renaming as an array [output length, output string, informations]\n\t */\n\thashObjectProperties : function(inputData, objectNames, objectOffsets, objectDeclarationLengths, referenceProperties, varsNotReassigned)\n\t{\t\t\t\t\n\t\tvar input = inputData.contents;\n\t\tvar details = inputData.log;\n\t\tvar propertiesInUseByContext=[];\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t{\n\t\t\tvar propertiesInUse = [];\n\t\t\t// almost the same expression as for methods, without the final \"(\"\n\t\t\tvar exp = new RegExp(\"(^|[^\\\\w$])\"+objectNames[contextIndex]+\"\\\\.(\\\\w*)\",\"g\");\n\t\t\tvar result=exp.exec(input);\n\t\t\twhile (result) {\t// get a set with a unique entry for each property\n\t\t\t\tif (propertiesInUse.indexOf(result[2])==-1) { \n\t\t\t\t\tpropertiesInUse.push(result[2]);\t\n\t\t\t\t}\n\t\t\t\tresult=exp.exec(input);\n\t\t\t}\n\t\t\tpropertiesInUseByContext.push(propertiesInUse);\n\t\t}\n\n\t\t\n\t\tvar bestTotalScore = -999, bestIndex =-1, bestYValue = 0, bestXValue = 0, bestScoreByContext = [];\n\t\t// For each hashing function, we compute the hashed names of all properties of the context object\n\t\t// All collisions (such as taking the first letter of scale() and save() end up in s()) are eliminated\n\t\t// as the order depends on the browser and we cannot assume the browser that will be running the final code\n\t\t// A score is then assigned to the hashing function :\n\t\t//   - for each context, the algorithm computes the gain obtained by shortening the non-colliding property names to their hash\n\t\t//     (the number of occurrences is irrelevant, we assume that the compression step will amount them to one)\n\t\t//   - the length of the hashing function is subtracted\n\t\t// Only the hashing function with the best score is kept - and applied\n\t\tfor (var functionIndex=0; functionIndex<this.hashFunctions.length; ++functionIndex) {\n\t\t\tvar functionDesc = this.hashFunctions[functionIndex];\n\t\t\tfor (var xValue=functionDesc[1]; xValue<=functionDesc[2] ; ++xValue) {\n\t\t\t\tfor (var yValue=functionDesc[3]; yValue<=functionDesc[4] ; ++yValue) {\n\t\t\t\t\tvar reverseLookup = [], forwardLookup = [];\n\t\t\t\t\tfor (var index=0; index<referenceProperties.length; ++index) {\n\t\t\t\t\t\tvar w = referenceProperties[index];\n\t\t\t\t\t\tvar hashedName = functionDesc[5].call(null, w, xValue, yValue);\n\t\t\t\t\t\t// a collision means that the hash is unsafe to use\n\t\t\t\t\t\t//  - either another property/method is hashed to the same string\n\t\t\t\t\t\t//  - or an unhashed method used in the code has the same name\n\t\t\t\t\t\t//    (may happen for short names such as arc())\n\t\t\t\t\t\t//    We do not care about methods not used in the code, they can be safely overwritten\n\t\t\t\t\t\tvar collision = propertiesInUse.indexOf(hashedName)!=-1 || reverseLookup[hashedName];\n\t\t\t\t\t\treverseLookup[hashedName] = (collision ? \"<collision>\" : w);\n\t\t\t\t\t}\n\t\t\t\t\tfor (w in reverseLookup) {\n\t\t\t\t\t\tforwardLookup[reverseLookup[w]]=w;\t// keep only the property names with no collisions\n\t\t\t\t\t}\n\t\t\t\t\tvar allScores = [], totalScore = 0;\n\t\t\t\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t\t\t\t{\n\t\t\t\t\t\tvar score = 0;\n\t\t\t\t\t\tvar propertiesInUse = propertiesInUseByContext[contextIndex];\n\t\t\t\t\t\tfor (var propertyIndex=0; propertyIndex<propertiesInUse.length; ++propertyIndex) {\n\t\t\t\t\t\t\t// Fix for issue #11 : in FF, arrays have a method fill(), as 2D contexts do\n\t\t\t\t\t\t\t// typeof() discriminates between \"string\" (hash match), \"undefined\" (no match) and \"function\" (array built-in)\n\t\t\t\t\t\t\tif (typeof(forwardLookup[propertiesInUse[propertyIndex]])==\"string\") {\n\t\t\t\t\t\t\t\tscore += propertiesInUse[propertyIndex].length - forwardLookup[propertiesInUse[propertyIndex]].length;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tallScores.push(score); \n\t\t\t\t\t\tscore = Math.max(0, score); // if the gain is negative, no replacement will be performed\n\t\t\t\t\t\ttotalScore += score;\n\t\t\t\t\t}\n\t\t\t\t\t// the score for the hash is the gain as computed above,\n\t\t\t\t\t// minus the length of the hash function itself.\n\t\t\t\t\ttotalScore-=functionDesc[0].replace(/x/g, xValue).replace(/y/g, yValue).length;\n\t\t\t\t\t\n\t\t\t\t\t// Debug log\n\t\t\t\t\t// details +=functionDesc[0].replace(/x/g, xValue).replace(/y/g, yValue) + \" : \"+totalScore +\"\\n\";\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tif (totalScore>bestTotalScore) {\n\t\t\t\t\t\tbestTotalScore = totalScore;\n\t\t\t\t\t\tbestScoreByContext = allScores;\n\t\t\t\t\t\tbestIndex = functionIndex;\n\t\t\t\t\t\tbestXValue = xValue;\n\t\t\t\t\t\tbestYValue = yValue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// bail out early if no gain\n\t\tif (bestTotalScore < 0) {\n\t\t\tdetails += \"Best hash loses \"+(-bestTotalScore)+\" bytes, skipping.\\n\";\n\t\t\treturn [this.getByteLength(input), input, details];\n\t\t}\n\t\t\n\t\t// best hash function (based on byte gain) found. Apply it\n\t\tvar reverseLookup = [], forwardLookup = [];\n\t\tfor (var index=0; index<referenceProperties.length; ++index) {\n\t\t\tvar w = referenceProperties[index];\n\t\t\tvar hashedName = this.hashFunctions[bestIndex][5].call(null, w, bestXValue, bestYValue);\n\t\t\treverseLookup[hashedName] = (reverseLookup[hashedName] ? \"<collision>\" : w);\n\t\t}\n\t\tfor (w in reverseLookup) {\n\t\t\tforwardLookup[reverseLookup[w]]=w;\n\t\t}\n\t\tvar hashedCode = input;\n\t\t\n\t\t// Tell the user what is being replaced, and what is ignored\n\t\tvar renamedList = \"\", notRenamedList = \"\";\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex)\n\t\t{\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\n\t\t\t\trenamedList += (renamedList.length>0?\", \" : \"\") + objectNames[contextIndex];\n\t\t\t} else {\n\t\t\t\tnotRenamedList += (notRenamedList.length>0?\", \" : \"\") + objectNames[contextIndex];\n\t\t\t}\n\t\t}\n\t\tif (notRenamedList.length>0) {\n\t\t\tdetails += \"No renaming for \"+notRenamedList+\"\\n\";\n\t\t}\n\t\tif (renamedList.length>0) {\n\t\t\tdetails += \"Renamed properties for \"+renamedList+\"\\n\";\n\t\t}\n\n\t\t// Determine the context with the shortest name\n\t\t// It will be used to store the hashes\n\t\t// (at the declaration of the last context that gets hashed).\n\t\tvar shortestContext = 0;\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (objectNames[contextIndex].length <  objectNames[shortestContext].length) {\n\t\t\t\tshortestContext = contextIndex;\n\t\t\t}\n\t\t}\n\n\t\t\n\t\t// Perform the replacement inside the code\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\t// replace only if the gain is positive\n\t\t\t\tvar propertiesInUse = propertiesInUseByContext[contextIndex];\n\t\t\t\tfor (var propertyIndex=0; propertyIndex<propertiesInUse.length; ++propertyIndex) {\n\t\t\t\t\n\t\t\t\t\t// Fix for issue #11, needed in this iteration again\n\t\t\t\t\tif (typeof(forwardLookup[propertiesInUse[propertyIndex]])==\"string\") {\n\t\t\t\t\t\t// replace all instances of that property (\"g\" option in the RegExp)\n\t\t\t\t\t\t// The nonstring character at the end avoids triggering on a subset of the name,\n\t\t\t\t\t\t// for instance enable() instead of enableVertexAttribArray() in WebGL contexts\n\t\t\t\t\t\t// (see test case webglContext_substringHash.js)\n\t\t\t\t\t\t// The initial character (or line start) avoids triggering if one context has a name\n\t\t\t\t\t\t// ending in another context's name (such as 'c' and 'cc')\n\t\t\t\t\t\tvar exp = new RegExp(\"(^|[^\\\\w$])\"+objectNames[contextIndex]+\"\\\\.\"+propertiesInUse[propertyIndex]+\"($|\\\\W)\",\"g\");\t\t\t\t\t\t\n\t\t\t\t\t\tvar originalText = \".\"+propertiesInUse[propertyIndex];\n\t\t\t\t\t\tvar replacementText = \"[\"+objectNames[shortestContext]+\".\"+forwardLookup[propertiesInUse[propertyIndex]]+\"]\";\n\t\t\t\t\t\t//hashedCode = hashedCode.replace(exp, \"$1\"+objectNames[contextIndex]+\"[\"+objectNames[shortestContext]+\".\"+forwardLookup[propertiesInUse[propertyIndex]]+\"]$2\");\n\t\t\t\t\t\thashedCode = this.stringHelper.matchAndReplaceAll(hashedCode, exp, originalText, replacementText, \"\", \"\", 0, inputData.thermalMapping);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// show the renaming in the details, for used properties only\n\t\t\t\t\t\tdetails += objectNames[contextIndex]+\".\"+forwardLookup[propertiesInUse[propertyIndex]] + \" -> \" + propertiesInUse[propertyIndex] + \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdetails += \"\\n\";\n\t\t\n\t\t// Choose the index variable for the hashing loop\n\t\tvar loopVarResult = this.getMostFrequentLoopVariable(input, varsNotReassigned);\n\t\tvar indexName = loopVarResult[0];\n\t\tdetails += loopVarResult[1]; // operation log\t\t\n\n\t\t// Create the final hashing expression by replacing the placeholder variables\n\t\tvar expression = this.hashFunctions[bestIndex][0].replace(/w/g, indexName);\n\t\texpression = expression.replace(/x/g, bestXValue);\t\t\n\t\texpression = expression.replace(/y/g, bestYValue);\t\t\n\t\t// If the input code uses mostly \"\" as string delimiters, use it as well in the expression instead of ''\n\t\tif (input.split('\"').length>input.split(\"'\").length) {\n\t\t\texpression = expression.replace(/'/g, '\"');\n\t\t}\n\n\t\t// Determine where in the code the hashing loop will be inserted.\n\t\t// (at the declaration of the last context that gets hashed).\n\t\tvar offset = 0, loopContext = 0;\n\t\tfor (var contextIndex=0; contextIndex<objectNames.length; ++contextIndex) {\n\t\t\tif (bestScoreByContext[contextIndex]>=0) {\t// replace only if the gain is positive\n\t\t\t\toffset = objectOffsets[contextIndex];\t// perform the replacement at the latest context definition\n\t\t\t\tloopContext = contextIndex;\n\t\t\t}\n\t\t}\n\t\tvar outputIntro = hashedCode.substr(0, offset);\n\t\t\n\t\t// Insert the hashing/renaming loop in the code.\n\t\t// If the context definition is not included (js1k shim for instance), the loop is prepended to the code and ends with \";\"\n\t\t// otherwise the loop replaces and includes the last declaration. The code looks like :\n\t\t//   for (i in c=a.getContext('2d'))c[...]=i with a single context\n\t\t//   for (i in c=a.getContext('2d'))c[...]=b[...]=i with multiple contexts\n\t\t// The ending separator is kept, unless it is a comma \",\", in which case it is replaced with a semicolon \";\"\n\t\t// (to avoid including the trailing code in the loop)\n\t\tvar declarationLength = objectDeclarationLengths[loopContext];\n\t\tvar outputInitBlock1 = \"for(\"+indexName+\" in \"+(declarationLength==0?objectNames[loopContext]:\"\");\n\t\tvar outputInitBlock2 = (declarationLength==0?\"\":hashedCode.substr(offset, declarationLength));\n\t\tvar outputInitBlock3 = \")\";\n\t\t//output+=\"for(\"+indexName+\" in \"+(declarationLength==0?objectNames[loopContext]:hashedCode.substr(offset, declarationLength))+\")\";\n\t\toutputInitBlock3+=objectNames[shortestContext]+\"[\"+expression+\"]=\"+indexName;\n\t\tif (hashedCode[offset+declarationLength]==\",\") {\n\t\t\t// replace the trailing \",\" with \";\" as explained above\n\t\t\toutputInitBlock3+=\";\";\n\t\t\t++declarationLength;\n\t\t}\n\t\toutputInitBlock3+=(declarationLength==0?\";\":\"\");\n\t\tvar outputMain = hashedCode.substr(offset+declarationLength);\n\t\tvar output = outputIntro + outputInitBlock1 + outputInitBlock2 + outputInitBlock3 + outputMain;\n\t\t\n\t\t// Record the renaming loop in the thermal transform\n\t\tvar transform = [ { inLength : hashedCode.length, outLength : output.length, complete : true } ];\n\t\tif (offset) {\n\t\t\t// code before the context declaration and renaming loop, kept unchanged\n\t\t\ttransform.push ( { chapter : 0, rangeIn : [0, offset], rangeOut : [0, offset] });\n\t\t}\n\t\tvar rangeOutBegin = offset;\n\t\tif (declarationLength) {\t// context declaration done inside the code\n\t\t\t// \"for (i in \" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [offset, outputInitBlock1.length] });\n\t\t\trangeOutBegin += outputInitBlock1.length;\n\t\t\t// \"context=canvas.getContext('2d')\" : map as is\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset, outputInitBlock2.length],\n\t\t\t\t\t\t\t   rangeOut : [rangeOutBegin, outputInitBlock2.length] });\n\t\t\trangeOutBegin += outputInitBlock2.length;\n\t\t\t// \")c[...]=i\" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [rangeOutBegin, outputInitBlock3.length] });\n\t\t\trangeOutBegin += outputInitBlock3.length;\n\t\t\n\t\t} else {\t// context declaration done beforehand (in the shim)\n\t\t\t// \"for (i in c)c[...]=i;\" : map to entire hashed code\n\t\t\ttransform.push ( { chapter : 0, \n\t\t\t\t\t\t\t   rangeIn : [offset, outputMain.length], \n\t\t\t\t\t\t\t   rangeOut : [offset, outputInitBlock1.length+outputInitBlock2.length+outputInitBlock3.length] });\n\t\t\trangeOutBegin += outputInitBlock1.length+outputInitBlock2.length+outputInitBlock3.length;\n\t\t}\n\t\t// code using the hashed contexts\n\t\ttransform.push( { \tchapter : 0, \n\t\t\t\t\t\t\trangeIn : [offset+declarationLength, outputMain.length], \n\t\t\t\t\t\t\trangeOut : [rangeOutBegin, outputMain.length] });\n\t\t\n\t\tinputData.thermalMapping.push(transform);\n\n\n\t\t// output stored in inputData parameter\n\t\tinputData.contents = output;\n\t\tinputData.log = details;\n\t},\n\n\t\n\t/**\n\t * Returns the total byte length of a string\n\t *  1 for ASCII char\n\t *  3 for Unicode (UTF-8)\n\t * Issue #5 : final size when featuing unicode characters\n\t *\n\t * @param inString the string to measure\n\t * @return the UTF-8 length of the string, in bytes \n\t */\n\tgetByteLength : function (inString)\n\t{\n\t\treturn encodeURI(inString).replace(/%../g,'i').length;\n\t},\n\t\n\t/**\n\t * Returns true if the character code is allowed in the name of a variable.\n\t * Allowed codes are 36($), 48-57(0-9), 65-90(A-Z), 95(_), 97-122(a-z)\n\t *\n\t * @param charCode ASCII code of the character (variables with Unicode > 127 are not accepted)\n\t * @return true if the character is allowed in the name of a variable, false otherwie\n\t */\n\tisCharAllowedInVariable : function (charCode)\n\t{\n\t\treturn (charCode>64 && charCode<91) || (charCode>96 && charCode<123) \n\t\t\t   || (charCode>47 && charCode<58)\n\t\t\t   || charCode==36 || charCode==95;\n\t},\n\n\t/**\n\t * Returns true if the character code is a digit\n\t * @param charCode ASCII or Unicode value of the character\n\t * @return true for digits (0 to 9 = ASCII 48 to 57), false otherwise\n\t */\n\tisDigit : function (charCode)\n\t{\n\t\treturn (charCode>47 && charCode<58);\n\t},\n\t\n\t/**\n\t * Identifies and returns the one-letter variable that is the\n\t * most common occurrence as a loop variable in a for(..;..;..) loop\n\t *\n\t * If no loop is found in the code, returns \"i\" as default.\n\t * Protected variables (from param varsNotReassigned) are not counted\n\t * as they should not be reassigned (issue #9), as the goal is to\n\t * create another for loop using the same variabled.\n\t *\n\t * Called by both hashing methods to assign a name to the \n\t * renaming loop, in order to benefit most from compression.\n\t * @see renameObjectMethods\n\t * @see hashObjectProperties\n\t *\n\t * @param input the code to parse for loop variables\n\t * @param varsNotReassigned (from options) boolean array[128], true to avoid altering variable\n\t * @return an array [ variable name (String), log ]\n\t */\n\tgetMostFrequentLoopVariable : function(input, varsNotReassigned)\n\t{\n\t\t// Choose the index variable for the added hashing code\n\t\t// The algorithm counts every instance of \"for(*\" with individual letters replacing the star\n\t\t// then chooses the letter with the most matches, in order to benefit most from compression\n\t\tvar log =\"Loop variables :\\n\";\n\t\tvar indexMatches = new Array(128) ;\n\n\t\t// #58 : only choose legal variable names, not digits\n\t\tvar loopIndexRegExp = /for\\(([A-Za-z_$])/g;\n\t\tvar loopIndexResult=loopIndexRegExp.exec(input);\n\t\twhile (loopIndexResult) {\t// get a set with a unique entry for each property\n\t\t\tvar code = loopIndexResult[1].charCodeAt(0);\n\t\t\tif (!varsNotReassigned[code]) {\t// issue #9 : skip protected variable\n\t\t\t\tindexMatches[code] = (indexMatches[code]||0)+1;\n\t\t\t}\n\t\t\tloopIndexResult=loopIndexRegExp.exec(input);\n\t\t}\n\t\tvar indexName=\"i\"; // default name\n\t\tvar maxMatches = 0;\n\t\tfor (var i=0; i<128; ++i) {\n\t\t\tif (indexMatches[i]>maxMatches) {\n\t\t\t\tmaxMatches = indexMatches[i];\n\t\t\t\tindexName = String.fromCharCode(i);\n\t\t\t}\n\t\t}\n\t\tfor (var i=0; i<128; ++i) {\n\t\t\tif (indexMatches[i]) {\n\t\t\t\tlog += String.fromCharCode(i)+\" *\"+indexMatches[i]+(indexName == String.fromCharCode(i)?\" <-- selected\":\"\")+\"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (maxMatches == 0) {\n\t\t\tlog += \"No relevant loop found, defaulting to \"+indexName+\"\\n\";\n\t\t}\n\t\tlog += \"\\n\";\n\t\treturn [ indexName, log ];\n\t},\n\t\n\t/**\n\t * Defines and returns a packer-friendly name for a new variable.\n\t * \n\t * It first lists characters used in keywords but not in existing variables.\n\t * If none is found, it takes the first character not assigned to a variable.\n\t * If none is available, it returns a two-letter variable.\n\t * \n\t * @param inputData (constant) PackerData structure containing the input code\n\t * @param options options set, used by discriminateKeywordsAndVariables()\n\t * @return the name of the new variable, as a string\n\t * @see discriminateKeywordsAndVariables\n\t */\n\tallocateNewVariable : function(inputData, options)\n\t{\n\t\tvar keywordsAndVariables = this.discriminateKeywordsAndVariables(inputData, options);\n\t\tvar keywordChars = keywordsAndVariables[0];\n\t\tvar variableChars = keywordsAndVariables[1];\n\t\tvar availableChars = keywordsAndVariables[2];\n\t\t\n\t\t// first, characters already used by functions, keywords ..\n\t\tfor (var i=33; i<128; ++i) {\n\t\t\tif (availableChars[i]) {\n\t\t\t\treturn String.fromCharCode(i);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// then, one-letter names not used by variables\n\t\tfor (var i=33; i<128; ++i) {\n\t\t\tif (!variableChars[i] && this.isCharAllowedInVariable(i) && !this.isDigit(i)) {\n\t\t\t\treturn String.fromCharCode(i);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// if still not, try two-letter names\n\t\tvar input = inputData.contents;\n\t\tfor (var i=97; i<122; ++i) {\n\t\t\tfor (var j=97; j<122; ++j) {\n\t\t\t\tname = String.fromCharCode(i,j);\n\t\t\t\tif (input.indexOf(name)==-1) {\n\t\t\t\t\treturn name;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn \"__\";\n\t},\n\t\n\t/**\n\t * Identify the characters used in user variable names\n\t * and those used in keywords. Unicode characters are not supported.\n\t *\n\t * The result can be used for renaming variables or to\n\t * allocate a new variable name\n\t * @see reassignVariableNames\n\t * @see allocateNewVariable\n\t * \n\t * @param inputData PackerData structure containing the setup and the code to process\n\t * @param options options set, see below for use details\n\t * Options used are :\n\t *  - varsNotReassigned : boolean array[128], true to avoid altering variable\n\t * @return array [ keywords, variables, available for new variables, variables only ], each is a boolean [128]\n\t *  - keywords : true if the character is present in keywords, false otherwise\n\t *  - variables : true if the character is already used as a variable, false otherwise\n\t *  - available : true if the character is present in keywords but not used as a variable, false otherwise\n\t */\n\tdiscriminateKeywordsAndVariables : function(inputData, options)\n\t{\n\t\tvar varsNotReassigned = options.varsNotReassigned;\n\t\tvar variableChars = [], keywordChars = [], availableChars = [], variableCharsOnly = [];\n\t\tvar previousChar = 0;\n\t\tvar letterCount = 0;\n\t\tvar isKeyword = false;\n\t\tvar input = inputData.contents;\n\t\tfor (var i=0; i<128; ++i) {\n\t\t\tvariableChars[i] = keywordChars[i] = availableChars[i] = variableCharsOnly[i] = false;\n\t\t}\n\t\t// Identify characters used in the code :\n\t\t//  - those used only in keywords, method names.\n\t\t//  - those used as one-letter variable or function names only (and candidates to renaming)\n\t\t//  - those used for both variable names and within keywords\n\n\t\tvar stringIndex = 0, templateLiteralIndex = 0;\n\t\tfor (var offset=0; offset<input.length; ++offset) {\n\t\t\t\n\t\t\tvar currentChar = input.charCodeAt(offset);\n\t\t\tif (currentChar<128) {\n\t\t\t\tif (this.isCharAllowedInVariable(currentChar)) {\n\t\t\t\t\n\t\t\t\t\twhile (stringIndex < inputData.containedStrings.length && inputData.containedStrings[stringIndex].end < offset) {\n\t\t\t\t\t\t++stringIndex;\n\t\t\t\t\t}\n\t\t\t\t\twhile (templateLiteralIndex < inputData.containedTemplateLiterals.length && inputData.containedTemplateLiterals[templateLiteralIndex].end < offset) {\n\t\t\t\t\t\t++templateLiteralIndex;\n\t\t\t\t\t}\n\t\t\t\t\tvar insideString = (stringIndex < inputData.containedStrings.length \n\t\t\t\t\t\t&& inputData.containedStrings[stringIndex].begin < offset\n\t\t\t\t\t\t&& offset < inputData.containedStrings[stringIndex].end);\n\t\t\t\t\tvar insideTemplateLiteral = (templateLiteralIndex < inputData.containedTemplateLiterals.length \n\t\t\t\t\t\t&& inputData.containedTemplateLiterals[templateLiteralIndex].begin < offset\n\t\t\t\t\t\t&& offset < inputData.containedTemplateLiterals[templateLiteralIndex].end);\n\t\t\t\t\t\n\t\t\t\t\tif (insideString && !insideTemplateLiteral) {\n\t\t\t\t\t\t// #76 : characters inside a string, out of a template literal ${...}, are not variables\n\t\t\t\t\t\tkeywordChars[currentChar]=true;\n\t\t\t\t\t} else { // in a template literal, or not in a string (variables allowed)\n\t\t\t\t\t\t++letterCount;\n\t\t\t\t\t\tif (letterCount>1) {\n\t\t\t\t\t\t\tisKeyword=true;\n\t\t\t\t\t\t\tkeywordChars[previousChar]=true;\n\t\t\t\t\t\t\tkeywordChars[currentChar]=true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (previousChar == 46) { // character .\n\t\t\t\t\t\t\t\tisKeyword=true;\n\t\t\t\t\t\t\t\tkeywordChars[currentChar]=true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// only consider one-letter variables or functions. \n\t\t\t\t\t// Do not include digits, they are not permitted as one-letter variable names.\n\t\t\t\t\t// Do not include in variables if preceded by a dot, which would indicate member variable or function\n\t\t\t\t\tif (letterCount==1 && !this.isDigit(previousChar) && !isKeyword) {\n\t\t\t\t\t\tvariableChars[previousChar]=true;\n\t\t\t\t\t}\n\t\t\t\t\tletterCount=0;\n\t\t\t\t\tisKeyword=false;\n\t\t\t\t}\n\t\t\t\tpreviousChar=currentChar;\n\t\t\t}\n\t\t}\n\n\t\t// Identify as available all characters used in keywords but not variables\n\t\t// And those that should be reassigned, if the options is set\n\t\t// #86 : factored here as available variable names are used in multiple places\n\t\tfor (var i=33; i<128; ++i) {\n\t\t\tavailableChars[i] = keywordChars[i] && !variableChars[i] && this.isCharAllowedInVariable(i) && !this.isDigit(i);\n\t\t\tvariableCharsOnly[i] = variableChars[i] && !keywordChars[i] && !varsNotReassigned[i];\n\t\t}\n\t\t\n\t\treturn [ keywordChars, variableChars, availableChars, variableCharsOnly ];\n\t},\n\t\n\t/**\n\t * Renames one-letter variables in the code, in order to group characters in consecutive blocks as much as possible.\n\t * This will leave larger empty blocks for tokens, so that the character class that represents them\n\t * in the final regular expression will be shorter : a block is represented by three characters (begin,\n\t * dash, end), no matter now many are comprised in between.\n\t * This operation does not change the length of the code nor its inner workings.\n\t *\n\t * The method :\n\t * - lists all the one-letter variables in the code\n\t * - identifies those using a character that is not otherwise present in classes or keywords\n\t *   (meaning that renaming them will actually free the character to use as a token)\n\t * - identifies all the characters in use by classes or language keywords\n\t * - reassign those to variables, if available (not used by other variables)\n\t * - if there are remaining variables in need of a name, fill gaps in the ASCII table, starting with the shortest ones.\n\t *   (for instance, if a,c,d,e,g,h are in use, it will assign b and f, since [a-h] is shorter than [ac-egh]\n\t *\n\t * @param inputData (in/out) PackerData structure containing the setup and the code to process\n\t * @param options options set, see below for use details\n\t * @return nothing. Result of refactoring is stored in parameter inputData.\n\t * Options used are :\n\t *  - varsNotReassigned : boolean array[128], true to avoid altering variable\n\t */\n\treassignVariableNames : function (inputData, options)\n\t{\n\t\tvar varsNotReassigned = options.varsNotReassigned;\n\t\tvar keywordsAndVariables = this.discriminateKeywordsAndVariables(inputData, options);\n\t\tvar keywordChars = keywordsAndVariables[0];\n\t\tvar variableChars = keywordsAndVariables[1];\n\t\tvar availableChars = keywordsAndVariables[2];\n\t\tvar variableCharsOnly = keywordsAndVariables[3];\n\t\tvar input = inputData.contents;\n\t\t\n\t\tvar details = \"----------- Renaming variables to optimize RegExp blocks --------\\n\";\n\t\tdetails += \"All variables : \";\n\t\t\n\t\tvar availableCharList = \"\";\n\t\tvar formerVariableCharList = \"\";\n\t\tfor (var i=32; i<128; ++i) {\n\t\t\tif (variableChars[i]) {\n\t\t\t\tdetails+=String.fromCharCode(i);\n\t\t\t}\n\t\t\t// Identify as available all characters used in keywords but not variables\n\t\t\tif (availableChars[i]) {\n\t\t\t\tavailableCharList+=String.fromCharCode(i);\n\t\t\t}\n\t\t\t// List all variables that can be reassigned a new name.\n\t\t\t// This excludes those with a one-letter name already used in a keyword (no gain in renaming them)\n\t\t\t// and those explicitely excluded from the process by the user.\n\t\t\tif (variableCharsOnly[i]) {\n\t\t\t\tformerVariableCharList+=String.fromCharCode(i);\n\t\t\t}\n\t\t}\n\t\tvar detailsSub1 = availableCharList.length ? availableCharList : \"(none)\";\n\t\tvar detailsSub2 = formerVariableCharList.length ? formerVariableCharList : \"(none)\";\n\t\tdetails +=\"\\n\";\n\t\tdetails += \"in keywords only : \" + detailsSub1 + \"\\nin variables only : \" + detailsSub2 + \"\\n\\n\";\n\n\t\t// Prepare to rename variables\n\t\t// If we have more variables to rename (characters used as variable names only)\n\t\t// than characters available (used in keywords only, but not as variables - yet),\n\t\t// we need to allocate more characters, among those not in use :\n\t\t//   - either those not used at all in the code\n\t\t//   - or those used by variables only (which means not renaming the variable by that name)\n\t\t// \n\t\t// The algorithm attempts to reduce the block count (i.e. chooses characters in between two blocks, that are thus merged)\n\t\t// so that the regular expression for tokens can be as short as possible.\n\t\tif (availableCharList.length < formerVariableCharList.length) {\n\t\t\tvar lettersNeeded = formerVariableCharList.length - availableCharList.length;\n\t\t\t\n\t\t\t// identify blocs of unused characters\n\t\t\tvar unusedBlocks = [];\n\t\t\tvar blockStartsAt = -1;\n\t\t\tfor (var i=1; i<128; ++i) \n\t\t\t{\n\t\t\t\tif (!keywordChars[i] && !varsNotReassigned[i]) {\n\t\t\t\t\t// not present in code, or used for naming variables only : add to candidate characters\n\t\t\t\t\t// variables not reassigned are not included in the pool (we do not want to rename another variable to that)\n\t\t\t\t\tif (blockStartsAt==-1) {\n\t\t\t\t\t\tblockStartsAt = i;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// present in code, ends block if started\n\t\t\t\t\tif (blockStartsAt!=-1) {\n\t\t\t\t\t\tunusedBlocks.push( {first:blockStartsAt, nextToLast:i});\n\t\t\t\t\t\tblockStartsAt = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fix for #94 : add the lask block if it was initialized\n\t\t\tif (blockStartsAt!=-1) {\n\t\t\t\tunusedBlocks.push( {first:blockStartsAt, nextToLast:i});\n\t\t\t}\n\t\t\t\n\t\t\t// There will always be enough characters, since we count those we are trying to eliminate\n\t\t\t// In the worst case, variables will be renamed to themselves, with no loss.\n\t\t\t// Sort the blocks, shortest to longest.\n\t\t\t// Fix for #29 : added tiebreaker (ASCII order) to get a consistent result\n\t\t\t// (weight of 1/1000 so it does not interfere with the main comparison)\n\t\t\tunusedBlocks.sort( function(a,b) { return (a.nextToLast-a.first)-(b.nextToLast-b.first)+.001*(a.first-b.first); } );\n\n\t\t\tdetailsSub1 = \"Adding letters : \";\n\t\t\tdetailsSub2 = \"Not renaming : \";\n\t\t\tvar blockIndex = 0;\n\t\t\twhile (lettersNeeded) {\n\t\t\t\tfor (var i=unusedBlocks[blockIndex].first;  lettersNeeded>0 && i<unusedBlocks[blockIndex].nextToLast; ++i) {\n\t\t\t\t\tif (this.isCharAllowedInVariable(i) && !this.isDigit(i)) {\n\t\t\t\t\t\tvar variableName = String.fromCharCode(i);\n\t\t\t\t\t\tvar indexInFormerList = formerVariableCharList.indexOf(variableName);\n\t\t\t\t\t\tif (indexInFormerList>-1) {\n\t\t\t\t\t\t\t// variable name already in use : do not rename it\n\t\t\t\t\t\t\tformerVariableCharList = formerVariableCharList.substr(0,indexInFormerList)\n\t\t\t\t\t\t\t\t\t\t\t\t\t+formerVariableCharList.substr(indexInFormerList+1);\n\t\t\t\t\t\t\tdetailsSub2+=variableName;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdetailsSub1+=variableName;\n\t\t\t\t\t\t\tavailableCharList+=variableName;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t--lettersNeeded;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t++blockIndex;\n\t\t\t}\n\t\t\tdetails+=detailsSub1+\"\\n\"+detailsSub2+\"\\n\";\n\t\t\t\n\t\t}\n\t\tdetails += formerVariableCharList.length?\"Renaming variables : \\n\":\"No variables to rename.\\n\";\n\t\tvar output = input;\n\t\t// Perform the replacement inside all relevant strings\n\t\tfor (var i=0; i<formerVariableCharList.length; ++i)\n\t\t{\n\t\t\tvar oldVarName = formerVariableCharList[i];\n\t\t\tvar exp = new RegExp(\"(^|[^\\\\w\\\\d$_])\"+(oldVarName==\"$\"?\"\\\\\":\"\")+oldVarName,\"g\");\n\t\t\t// #57 : replace if not in string, or if inside a substitution pattern in a `template literal`\n\t\t\t\n\t\t\tvar stringIndex = 0, templateLiteralIndex = 0;\n\t\t\tvar variableMatch = exp.exec(output);\n\t\t\twhile (variableMatch && variableMatch[0] != \"\") {\n\t\t\t\tvar offset = variableMatch.index+variableMatch[0].indexOf(oldVarName);\n\t\t\t\twhile (stringIndex < inputData.containedStrings.length && inputData.containedStrings[stringIndex].end < offset) {\n\t\t\t\t\t++stringIndex;\n\t\t\t\t}\n\t\t\t\twhile (templateLiteralIndex < inputData.containedTemplateLiterals.length && inputData.containedTemplateLiterals[templateLiteralIndex].end < offset) {\n\t\t\t\t\t++templateLiteralIndex;\n\t\t\t\t}\n\t\t\t\tvar insideString = (stringIndex < inputData.containedStrings.length \n\t\t\t\t\t&& inputData.containedStrings[stringIndex].begin < offset\n\t\t\t\t\t&& offset < inputData.containedStrings[stringIndex].end);\n\t\t\t\tvar insideTemplateLiteral = (templateLiteralIndex < inputData.containedTemplateLiterals.length \n\t\t\t\t\t&& inputData.containedTemplateLiterals[templateLiteralIndex].begin < offset\n\t\t\t\t\t&& offset < inputData.containedTemplateLiterals[templateLiteralIndex].end);\n\t\t\t\t\t\n\t\t\t\tif (insideTemplateLiteral || !insideString) {\t// perform replacement\n\t\t\t\t\toutput = output.substr(0, offset) + availableCharList[i] + output.substr(offset+1);\n\t\t\t\t}\n\t\t\t\tvariableMatch = exp.exec(output);\n\t\t\t}\n\n\t\t\t// Perform the replacement on the code appended by refactorToSetInterval()\n\t\t\tinputData.interpreterCall = inputData.interpreterCall.replace(exp, \"$1\"+availableCharList[i]);\n\t\t\tinputData.wrappedInit = inputData.wrappedInit.replace(exp, \"$1\"+availableCharList[i]);\n\t\t\t\n\t\t\t// replace the packed code holder definition and usage as well\n\t\t\t// (some code is designed to reuse the string - see JsCrush or Impossible Road)\n\t\t\tif (formerVariableCharList[i]==inputData.packedCodeVarName) {\n\t\t\t\tinputData.packedCodeVarName = availableCharList[i];\n\t\t\t}\n\t\t\tdetails += \"  \"+oldVarName+ \" => \"+availableCharList[i]+\"\\n\";\n\t\t}\n\t\t\n\t\t// output stored in inputData parameter instead of being returned\n\t\tinputData.contents = output;\n\t\tinputData.log += details;\t\t\n\t},\n\t\n\t/**\n\t * Recognize all strings and template literals inside the input code\n\t *\n\t * Offset to beginning and end are stored into a table, along with\n\t *  - delimiters used : \", ' or `(since ES6)\n\t *  - other delimiters present inside the string\n     *  - if the string definition and allocation is standalone, and could thus be extracted\t \n\t *\n\t * Fields from the PackerData are cleared before they are filled, making the function safe to call multiple times\n\t *\n\t * @param inputData (in/out) PackerData structure containing the setup and the code to process\n\t * @param silent Boolean, true to keep logs untouched, false to add strings to the logs\n\t * @return nothing. Result is stored inside parameter inputData (containedStrings and containedTemplateLiterals)\n\t */\n\tidentifyStrings : function (inputData, silent)\n\t{\n\t\tvar details = \"\\nStrings present in the code :\\n\";\n\t\tvar inString = false;\n\t\tvar currentString = false;\n\t\tvar currentTemplateLiteral = false;\n\t\tvar input = inputData.contents;\n\t\tvar escaped = false;\n\t\tvar insideTemplateLiteral = 0;\n\t\tvar currentChar = 0;\n\t\t\n\t\tinputData.containedStrings = [];\n\t\tinputData.containedTemplateLiterals = [];\n\n\t\tfor (var i=0; i<input.length; ++i) {\n\t\t\tvar formerChar = currentChar;\n\t\t\tcurrentChar = input.charCodeAt(i);\n\t\t\t// delimiters : 34 \" , 39 ' , 96 `\n\t\t\tif (currentChar==34 || currentChar==39 || currentChar==96) {\n\t\t\t\tif (currentString) {\n\t\t\t\t\tif (currentChar == currentString.delimiter && !escaped && !insideTemplateLiteral) {\n\t\t\t\t\t\t// found the match to the string begin\n\t\t\t\t\t\tcurrentString.end = i;\n\t\t\t\t\t\tinputData.containedStrings.push(currentString);\n\t\t\t\t\t\tdetails += \"(\"+currentString.begin+\"-\"+currentString.end+\") \";\n\t\t\t\t\t\tdetails += currentString.characterCount[39]+\"' \"+currentString.characterCount[34]+'\" '+currentString.characterCount[96]+\"` \";\n\t\t\t\t\t\tdetails += String.fromCharCode(currentString.delimiter)+input.substr(currentString.begin+1, currentString.end-currentString.begin-1)+String.fromCharCode(currentString.delimiter)+\"\\n\";\n\t\t\t\t\t\tcurrentString = false;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// another delimiter in the string, such as \"`\" or '\"'\n\t\t\t\t\t\t++currentString.characterCount[currentChar];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// start a new string\n\t\t\t\t\tinString = true;\n\t\t\t\t\tcurrentString = { begin:i, end:-1, delimiter:currentChar, characterCount:Array(128).fill(0) };\n\t\t\t\t}\n\t\t\t} \n\t\t\tif (formerChar==36 && currentChar==123 && currentString) { // 36 $, 123 {\n\t\t\t\tif (currentString.delimiter==96) { // 96 `\n\t\t\t\t\t// #82 : inside a template literal, backticks do not terminate the string\n\t\t\t\t\t++insideTemplateLiteral;\n\t\t\t\t\tif (insideTemplateLiteral == 1) {\n\t\t\t\t\t\tcurrentTemplateLiteral = { begin: i, end: -1 };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (currentChar==125 && currentString && insideTemplateLiteral>0) { // 125 }\n\t\t\t\tif (currentString.delimiter==96) { // 96 `\n\t\t\t\t\t// #82 : inside a template literal, backticks do not terminate the string\n\t\t\t\t\t--insideTemplateLiteral;\n\t\t\t\t\tif (!insideTemplateLiteral) {\n\t\t\t\t\t\tcurrentTemplateLiteral.end=i;\n\t\t\t\t\t\tinputData.containedTemplateLiterals.push(currentTemplateLiteral);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tescaped = (currentChar==92 && !escaped);\n\t\t}\n\t\t\n\t\tif (inputData.containedTemplateLiterals.length) {\n\t\t\tdetails += \"\\nTemplate literals present in the code :\\n\";\n\t\t}\n\t\tfor (var i=0; i<inputData.containedTemplateLiterals.length; ++i) {\n\t\t\tvar currentTemplateLiteral = inputData.containedTemplateLiterals[i];\n\t\t\tdetails += \"(\"+currentTemplateLiteral.begin+\"-\"+currentTemplateLiteral.end+\") \";\n\t\t\tdetails += input.substr(currentString.begin-1, currentString.end-currentString.begin+2)+\"\\n\";\n\t\t}\n\t\t\n\t\tif (!silent) {\n\t\t\tinputData.log+=details+\"\\n\";\n\t\t}\n\t\t\n\t},\n\t\n\t/**\n\t * Choose the delimiter for the final (packed) string\n\t *\n\t * Change the delimiters for strings inside as needed\n\t *\n\t * @param inputData (in/out) PackerData structure containing the setup and the code to process\n\t * @param options options set, see below for use details\n\t * @return nothing. Result is stored inside parameter inputData.\n\t * Options used are :\n\t *  useES6 : try ` as string delimiter if enables\n\t */\n\tquoteStrings : function (inputData, options) {\n\t\t// candidate delimiters\n\t\tvar allDelimiters = [ \"'\", '\"' ];\n\t\tif (options.useES6) {\n\t\t\tallDelimiters.push(\"`\");\n\t\t}\n\t\t\n\t\tvar bestCost = 999, bestDelimiter = '\"', bestNewStringQuotes = 0;\n\t\tvar details = \"\\Wrapping packed code in :\\n\";\n\t\tfor (var delimiter of allDelimiters) {\n\t\t\tvar cost = 0;\n\t\t\tvar delimCode = delimiter.charCodeAt(0);\n\t\t\tvar newStringQuotes = [];\n\t\t\tfor (var i=0; i<inputData.containedStrings.length; ++i) {\n\t\t\t\tcost += inputData.containedStrings[i].characterCount[delimCode] + (inputData.containedStrings[i].delimiter==delimCode ? 2 : 0);\n\t\t\t\t\n\t\t\t\tvar bestNewQuote = String.fromCharCode(inputData.containedStrings[i].delimiter);\n\t\t\t\tif (inputData.containedStrings[i].delimiter != 96) {\n\t\t\t\t\t// Attempt to regain bytes by changing the string delimiter \n\t\t\t\t\t// only if it is ' or \", not ` as this would disable template literals\n\t\t\t\t\t//  - gain all escapes from current delimiter being present inside the string\n\t\t\t\t\t//  - gain 2 if the current string delimiter matches the chosen one for the packed string (as it will not have to be escaped)\n\t\t\t\t\t//  - lose 1 for each copy of the new delimiter within the string\n\t\t\t\t\t//  - lose 2 if the new string delimiter matches the chosen one for the packed string (as it will have to be escaped)\n\t\t\t\t\tvar bestStringGain = 0; \n\t\t\t\t\tvar allStringDelimiters = [ \"'\", '\"' ];\n\t\t\t\t\t// only allow ` as delimiter if both ES6 flag is on, and the string does not contain \"${\"\n\t\t\t\t\t// (as it would be mistaken for an expression placeholder)\n\t\t\t\t\tvar placeholderIndex = inputData.contents.indexOf(\"${\", inputData.containedStrings[i].begin);\n\t\t\t\t\tif (options.useES6 && (placeholderIndex==-1 || placeholderIndex>inputData.containedStrings[i].end)) {\n\t\t\t\t\t\tallStringDelimiters.push(\"`\");\n\t\t\t\t\t}\n\t\t\t\t\tfor (var stringDelimiter of allStringDelimiters) {\n\t\t\t\t\t\tvar stringGain = 0;\n\t\t\t\t\t\tvar stringDelimCode = stringDelimiter.charCodeAt(0);\n\t\t\t\t\t\tstringGain = inputData.containedStrings[i].characterCount[inputData.containedStrings[i].delimiter];\n\t\t\t\t\t\tstringGain += inputData.containedStrings[i].delimiter==delimCode ? 2 : 0;\n\t\t\t\t\t\tstringGain -= inputData.containedStrings[i].characterCount[stringDelimCode];\n\t\t\t\t\t\tstringGain -= stringDelimCode==delimCode ? 2 : 0;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// give a slight malus when changing the delimiter of a string\n\t\t\t\t\t\t// so if the cost is tied, the solution that changes the least strings is preferred\n\t\t\t\t\t\tstringGain -= (inputData.containedStrings[i].delimiter == stringDelimCode ? 0 : 0.01);\n\t\t\t\t\t\tif (stringGain > bestStringGain) {\n\t\t\t\t\t\t\tbestNewQuote = stringDelimiter;\n\t\t\t\t\t\t\tbestStringGain = stringGain;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tcost -= bestStringGain;\n\t\t\t\t}\n\t\t\t\tnewStringQuotes.push(bestNewQuote);\n\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\tdetails += \" \"+delimiter+\" : cost = \"+cost+\"\\n\";\n\t\t\tif (cost < bestCost) {\n\t\t\t\tbestDelimiter = delimiter;\n\t\t\t\tbestCost = cost;\n\t\t\t\tbestNewStringQuotes = newStringQuotes.slice();\n\t\t\t}\n\t\t}\n\t\tinputData.packedStringDelimiter = bestDelimiter;\n\t\t\n\t\t// perform replacement in strings\n\t\t// we cannot use stringHelper.matchAndReplaceAll() as only some instances of the string are replaced in the code\n\t\tvar currentTransform = [];\n\t\tvar newInput = \"\", offset = 0;\n\t\tfor (var i=0; i<inputData.containedStrings.length; ++i) {\n\t\t\tvar currentString = inputData.containedStrings[i];\n\t\t\tlet intervalMapping = {\t// transform : iso mapping of the space between two strings\n\t\t\t\tchapter : 0,\n\t\t\t\trangeIn : [offset, currentString.begin-offset],\n\t\t\t\trangeOut: [newInput.length, currentString.begin-offset]\n\t\t\t};\n\t\t\tcurrentTransform.push(intervalMapping);\n\t\t\tnewInput += inputData.contents.substr(offset, currentString.begin-offset);\n\t\t\tvar newDelimiter = bestNewStringQuotes[i];\n\t\t\tvar currentDelimiter = String.fromCharCode(currentString.delimiter);\n\t\t\tlet stringMapping = { // transform : mapping of the string before / after\n\t\t\t\t// initialize with an iso mapping\n\t\t\t\tchapter : 0,\n\t\t\t\trangeIn : [currentString.begin, currentString.end-currentString.begin+1],\n\t\t\t\trangeOut: [newInput.length, currentString.end-currentString.begin+1]\n\t\t\t};\n\t\t\tif (currentDelimiter == newDelimiter) {\n\t\t\t\t// keep the delimiter for the current string \n\t\t\t\t// copy as is and keep the iso mapping\n\t\t\t\tnewInput += inputData.contents.substr(currentString.begin, currentString.end-currentString.begin+1);\n\t\t\t} else {\n\t\t\t\t// replace the delimiter, escape and unescape as needed\n\t\t\t\tvar exp = new RegExp(\"\\\\\"+currentDelimiter, \"g\");\n\t\t\t\tvar rawString = inputData.contents.substr(currentString.begin+1, currentString.end-currentString.begin-1);\n\t\t\t\trawString = rawString.replace(exp, currentDelimiter);\n\t\t\t\texp = new RegExp(newDelimiter, \"g\");\n\t\t\t\trawString = rawString.replace(exp, \"\\\\\"+newDelimiter);\n\t\t\t\tvar quote = (newDelimiter==inputData.packedStringDelimiter ? \"\\\\\" : \"\")+newDelimiter;\n\t\t\t\t\n\t\t\t\tdetails += \"(\"+currentString.begin+\"-\"+currentString.end+\") : \";\n\t\t\t\tdetails += String.fromCharCode(inputData.containedStrings[i].delimiter)+\" -> \"+newDelimiter+\"\\n\";\n\t\t\t\tinputData.containedStrings[i].begin = newInput.length;\n\t\t\t\tinputData.containedStrings[i].delimiter = newDelimiter;\n\t\t\t\tvar replacementString = quote + rawString + quote;\n\t\t\t\tstringMapping.rangeOut[1] = replacementString.length;\t// map to actual length\n\t\t\t\tnewInput += replacementString;\n\t\t\t\tinputData.containedStrings[i].end = newInput.length-1;\n\t\t\t\t\n\t\t\t}\n\t\t\toffset = currentString.end+1;\n\t\t\tcurrentTransform.push(stringMapping);\n\t\t\t\n\t\t}\n\t\tnewInput += inputData.contents.substr(offset);\n\t\tlet intervalMapping = {\t// transform : iso mapping of the space at the end\n\t\t\t\tchapter : 0,\n\t\t\t\trangeIn : [offset, inputData.contents.length-offset],\n\t\t\t\trangeOut: [newInput.length, inputData.contents.length-offset]\n\t\t\t};\n\t\tcurrentTransform.push(intervalMapping);\n\t\tinputData.contents = newInput;\n\t\t\n\t\tinputData.log+=details+\"\\n\";\t\t\n\t}\n\t\n}\n\n// Node.js exports (for packer)\nif (typeof require !== 'undefined') {\n\tmodule.exports = ShapeShifter;\n}\n"
  },
  {
    "path": "stringHelper.js",
    "content": "/**\n * StringHelper provides utility functions to operator on strings and regular expressions.\n * \n * It is stateless and implements the singleton pattern.\n *\n * JS singleton implementation based on\n * http://tassedecafe.org/fr/implementer-design-pattern-singleton-javascript-1023\n */\n \n\nvar StringHelper = (function() {\n\tvar constructor = function() {\n\t\n\t\n\t\t/**\n\t\t * Count bytes in a string's UTF-8 representation.\n\t\t * Code by 200_success at http://codereview.stackexchange.com/questions/37512/count-byte-length-of-string\n\t\t *\n\t\t * @param normal_val : input string\n\t\t * @return (int) string byte length\n\t\t */\n\t\tthis.getByteLength = function (normal_val) {\n\t\t\t// Force string type\n\t\t\tnormal_val = String(normal_val);\n\n\t\t\tvar byteLen = 0;\n\t\t\tfor (var i = 0; i < normal_val.length; i++) {\n\t\t\t\tvar c = normal_val.charCodeAt(i);\n\t\t\t\tbyteLen += c < (1 <<  7) ? 1 :\n\t\t\t\t\t\t   c < (1 << 11) ? 2 :\n\t\t\t\t\t\t   c < (1 << 16) ? 3 :\n\t\t\t\t\t\t   c < (1 << 21) ? 4 :\n\t\t\t\t\t\t   c < (1 << 26) ? 5 :\n\t\t\t\t\t\t   c < (1 << 31) ? 6 : Number.NaN;\n\t\t\t}\n\t\t\treturn byteLen;\n\t\t}\n\t\t\n\t\t/**\n\t\t * Count bytes in a character's UTF-8 representation inside a string\n\t\t * Code similar to getByteLength() except for character \\ thar must be escaped and thus costs 2\n\t\t * @see getByteLength\n\t\t * @param unicode : character's Unicode valude\n\t\t * @return (int) character byte length\n\t\t */\n\t\tthis.getCharacterLength = function (unicode) {\n\t\t\tvar byteLen = unicode < (1 <<  7) ? 1 :\n\t\t\t\t\t\t  unicode < (1 << 11) ? 2 :\n\t\t\t\t\t\t  unicode < (1 << 16) ? 3 :\n\t\t\t\t\t\t  unicode < (1 << 21) ? 4 :\n\t\t\t\t\t\t  unicode < (1 << 26) ? 5 :\n\t\t\t\t\t\t  unicode < (1 << 31) ? 6 : Number.NaN;\n\t\t\tbyteLen += (unicode==92 ? 1 : 0);\n\t\t\treturn byteLen;\n\t\t}\n\t\t \n\t\t\n\t\t/**\n\t\t * Encode a string to base64\n\t\t * Replacement for btoa() which does not handle correctly characters beyond 0xff\n\t\t *\n\t\t * Code from https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding\n\t\t * See also http://stackoverflow.com/questions/246801/how-can-you-encode-a-string-to-base64-in-javascript\n\t\t *\n\t\t * @param str String to encode\n\t\t * @return base64 representation of the string\n\t\t */\n\t\tthis.unicodeToBase64 = function(str) {\n\t\t\treturn btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {\n\t\t\t\treturn String.fromCharCode('0x' + p1);\n\t\t\t}));\n\t\t}\n\n\t\t/**\n\t\t * Decode a string from base64\n\t\t * Replaces atob() which does not handle correctly characters beyond 0xff\n\t\t *\n\t\t * Code from https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding\n\t\t *\n\t\t * @param str base64 string to decode\n\t\t * @return original decoded string\n\t\t */\n\t\tthis.base64ToUnicode = function(str) {\n\t\t\treturn decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {\n\t\t\t\treturn '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n\t\t\t}).join(''));\n\t\t}\n\n\t\t/**\n\t\t * Returns whether the contents of the input at the given offset is a string or actual code\n\t\t * Actual code means either :\n\t\t *  - outside of a string\n\t\t *  - inside a string AND inside a template literal\n\t\t *\n\t\t * The provided PackerData must have been run through identifyStrings() first.\n\t\t * \n\t\t * @param offset byte offset within the input contents\n\t\t * @param inputData (constant) PackerData containing the input string and the string identification\n\t\t */\n\t\tthis.isActualCodeAt = function(offset, inputData) {\n\t\t\tvar stringIndex = 0, templateLiteralIndex = 0;\t\t\t\n\t\t\twhile (stringIndex < inputData.containedStrings.length && inputData.containedStrings[stringIndex].end < offset) {\n\t\t\t\t++stringIndex;\n\t\t\t}\n\t\t\twhile (templateLiteralIndex < inputData.containedTemplateLiterals.length && inputData.containedTemplateLiterals[templateLiteralIndex].end < offset) {\n\t\t\t\t++templateLiteralIndex;\n\t\t\t}\n\t\t\t\n\t\t\tvar insideString = (stringIndex < inputData.containedStrings.length \n\t\t\t\t&& inputData.containedStrings[stringIndex].begin < offset\n\t\t\t\t&& offset < inputData.containedStrings[stringIndex].end);\n\t\t\tvar insideTemplateLiteral = (templateLiteralIndex < inputData.containedTemplateLiterals.length \n\t\t\t\t&& inputData.containedTemplateLiterals[templateLiteralIndex].begin < offset\n\t\t\t\t&& offset < inputData.containedTemplateLiterals[templateLiteralIndex].end);\n\t\t\t\t\n\t\t\treturn (!insideString) || insideTemplateLiteral;\n\t\t}\n\t\t\n\t\t/**\n\t\t * Replace all instances of a substring, and record the changes in a transform function\n\t\t * Use instead of String.replace(/.../g) to get the mapping function needed for the heatwave view.\n\t\t *\n\t\t * Specific version of matchAndReplaceFirstAndAll where all occurrences are replaced with the same text\n\t\t * @see matchAndReplaceFirstAndAll\n\t\t *\n\t\t * @param input input string before replacements (unmodified)\n\t\t * @param matchExp regular expression to search for (false to match originalText as is)\n\t\t * @param originalText substring to replace within the regex match\n\t\t * @param replacementText substring to substitute to all instances of originalText\n\t\t * @param prefix string to prepend to the output, mapped to all matches (use \"\" if none)\n\t\t * @param suffix string to append to the output, mapped to all matches (use \"\" if none)\n\t\t * @param extraMapping string from another chapter, mapped to all matches, not added here (false if none)\n\t\t * @param thermalMap array of all successive mapping functions (modified)\n\t\t * @return the string with all replacements performed\n\t\t */\n\t\tthis.matchAndReplaceAll = function(input, matchExp, originalText, replacementText, prefix, suffix, extraMapping, thermalMap) {\n\t\t\treturn this.matchAndReplaceFirstAndAll(input, matchExp, originalText, replacementText, replacementText, prefix, suffix, extraMapping, thermalMap);\n\t\t}\n\n\t\t\n\t\t/**\n\t\t * Replace all instances of a substring, and record the changes in a transform function\n\t\t * Use instead of String.replace(/.../g) to get the mapping function needed for the heatwave view.\n\t\t *\n\t\t * The matching operation is either done with a string search (set matchExp to false)\n\t\t * or with a regular expression that is first matched, then the originalText is\n\t\t * searched and replaced inside the regex match.\n\t\t * \n\t\t * The crusher and packer can prepend and append a dictionary entry to the string.\n\t\t * In the mapping, this prefix or suffix is mapped to all replaced strings.\n\t\t *\n\t\t * The first match has a different replacement string than the subsequent ones\n\t\t * This is intended to define a dictionary entry or a variable allocation in the first one.\n\t\t * In this case, the extra characters (the cost of the allocation) are mapped to all replaced strings.\n\t\t *\n\t\t * @param input input string before replacements (unmodified)\n\t\t * @param matchExp regular expression to search for (false to match originalText as is)\n\t\t * @param originalText substring to replace within the regex match\n\t\t * @param firstReplacementText substring to substitute to the first instance of originalText\n\t\t * @param otherReplacementsText substring to substitute to all but the first instance of originalText\n\t\t * @param prefix string to prepend to the output, mapped to all matches (use \"\" if none)\n\t\t * @param suffix string to append to the output, mapped to all matches (use \"\" if none)\n\t\t * @param extraMapping string from another chapter, mapped to all matches, not added here (false if none)\n\t\t * @param thermalMap array of all successive mapping functions (modified)\n\t\t * @return the string with all replacements performed\n\t\t */\n\t\tthis.matchAndReplaceFirstAndAll = function(input, matchExp, originalText, firstReplacementText, otherReplacementsText, prefix, suffix, extraMapping, thermalMap) {\n\t\t\tvar mappingFunction = [];\n\t\t\tvar allRangesIn = [];\n\t\t\tvar output = prefix;\n\t\t\tvar inputPointer = 0;\n\t\t\tvar originalTextLength = originalText.length;\n\t\t\tvar replacementTextLength = otherReplacementsText.length;\n\t\t\tvar initialAllocationLength = otherReplacementsText.length - firstReplacementText.length;\n\t\t\tvar firstReplacementMapping = false;\n\t\t\tvar replacedCopies = 0;\n\t\t\tvar offset = -1;\n\t\t\tif (matchExp) {\n\t\t\t\tlet nextMatch = matchExp.exec(input);\n\t\t\t\tif (nextMatch) {\n\t\t\t\t\tlet offsetInMatch = nextMatch[0].indexOf(originalText);\n\t\t\t\t\toffset = nextMatch.index + offsetInMatch;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\toffset = input.indexOf(originalText, inputPointer);\n\t\t\t}\n\t\t\twhile (offset >= 0) {\n\t\t\t\tif (offset>inputPointer) {\n\t\t\t\t\t// there is an interval between two replaced blocks. Register it into the mapping\n\t\t\t\t\tlet intervalMapping = {\n\t\t\t\t\t\tchapter : 0,\n\t\t\t\t\t\trangeIn : [inputPointer, offset-inputPointer],\n\t\t\t\t\t\trangeOut: [output.length, offset-inputPointer]\n\t\t\t\t\t};\n\t\t\t\t\tmappingFunction.push(intervalMapping);\n\t\t\t\t\toutput+= input.substring(inputPointer, offset);\n\t\t\t\t}\n\t\t\t\t// register the replaced text into the mapping\n\t\t\t\t// #86 : always use the length of the 2nd+ replacement,\n\t\t\t\t// as the extra from the first one, if any, corresponds to the allocation\n\t\t\t\t// and will be split on all matches on a dedicated mapping\n\t\t\t\tlet matchMapping = {\n\t\t\t\t\tchapter : 0,\n\t\t\t\t\trangeIn : [offset, originalTextLength],\n\t\t\t\t\trangeOut: [output.length, replacementTextLength] \n\t\t\t\t};\n\t\t\t\tmappingFunction.push(matchMapping);\n\t\t\t\tif (replacedCopies == 0 && initialAllocationLength > 0) {\n\t\t\t\t\t// #86 : mapping of the allocation (extra length from 1st replacement), later split on all matches\n\t\t\t\t\tfirstReplacementMapping = {rangeOut : [output.length+replacementTextLength, initialAllocationLength] };\n\t\t\t\t}\n\t\t\t\toutput+= replacedCopies==0 ? firstReplacementText : otherReplacementsText;\n\t\t\t\t++replacedCopies;\n\t\t\t\tallRangesIn.push(offset, originalTextLength);\n\t\t\t\t\n\t\t\t\tinputPointer = offset+originalTextLength;\n\t\t\t\tif (matchExp) {\n\t\t\t\t\tlet nextMatch = matchExp.exec(input);\n\t\t\t\t\tif (nextMatch) {\n\t\t\t\t\t\tlet offsetInMatch = nextMatch[0].indexOf(originalText);\n\t\t\t\t\t\toffset = nextMatch.index + offsetInMatch;\n\t\t\t\t\t} else {\n\t\t\t\t\t\toffset = -1;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\toffset = input.indexOf(originalText, inputPointer);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// text remaining at the end\n\t\t\tif (inputPointer < input.length) {\n\t\t\t\tlet intervalMapping = {\n\t\t\t\t\trangeIn : [inputPointer, input.length-inputPointer],\n\t\t\t\t\trangeOut: [output.length, input.length-inputPointer]\n\t\t\t\t};\n\t\t\t\tmappingFunction.push(intervalMapping);\n\t\t\t\toutput+= input.substring(inputPointer);\n\t\t\t}\n\t\t\t// Map prefix and suffix (if any) to all replaced blocks\n\t\t\tif (prefix != \"\") {\n\t\t\t\tlet prefixMapping = {\n\t\t\t\t\trangeIn : allRangesIn,\n\t\t\t\t\trangeOut : [0, prefix.length]\n\t\t\t\t};\n\t\t\t\tmappingFunction.push(prefixMapping);\n\t\t\t}\n\t\t\tif (suffix != \"\") {\n\t\t\t\tlet suffixMapping = {\n\t\t\t\t\trangeIn : allRangesIn,\n\t\t\t\t\trangeOut : [output.length, suffix.length]\n\t\t\t\t};\n\t\t\t\tmappingFunction.push(suffixMapping);\n\t\t\t}\n\t\t\tif (extraMapping) {\n\t\t\t\textraMapping.rangeIn = allRangesIn;\n\t\t\t\tmappingFunction.push(extraMapping);\n\t\t\t}\n\t\t\t// #86 : Map allocation from first replacement text to all matches\n\t\t\tif (firstReplacementMapping) {\n\t\t\t\tfirstReplacementMapping.rangeIn = allRangesIn;\n\t\t\t\tmappingFunction.push(firstReplacementMapping);\n\t\t\t}\n\t\t\toutput+= suffix;\n\t\t\t\n\t\t\tmappingFunction.unshift({ inLength : input.length, outLength : output.length, complete:true});\n\t\t\tthermalMap.push(mappingFunction);\n\t\t\t\n\t\t\treturn output;\n\t\t}\n\t\n\t\t/**\n\t\t * Returns the character matching the provided unicode value\n\t\t * as it should be displayed in a character class in a Regexp :\n\t\t *  - ] and \\ needs escaping\n\t\t *  - characters above 256 are \\u....\n\t\t *  - characters between 128 and 255 are \\x..\n\t\t *  - others (even below 32) are raw\n\t\t * @input charCode unicode value of the character to encode\n\t\t * @return formatted representation of the character for a RegExp char class\n\t\t */\n\t\tthis.writeCharToRegexpCharClass = function(charCode)\n\t\t{\n\t\t\tvar output = \"\";\n\t\t\tif (charCode>255) {\n\t\t\t\toutput = \"\\\\u\"+(charCode<4096?\"0\":\"\")+charCode.toString(16);\n\t\t\t} else if (charCode>127) {\n\t\t\t\toutput = \"\\\\x\"+charCode.toString(16);\n\t\t\t} else {\n\t\t\t\toutput = (this.needsEscapingInCharClass(charCode)?\"\\\\\":\"\")+String.fromCharCode(charCode);\n\t\t\t}\n\t\t\treturn output;\n\t\t}\n\n\n\t\n\t\t/**\n\t\t * Express a block as a range for a character class in a RegExp\n\t\t * @param first character code for the first character in range\n\t\t * @param last character code for the last character in range\n\t\t * @return a string representing the range inside a character class\n\t\t */\n\t\tthis.writeRangeToRegexpCharClass = function(first, last) {\n\t\t\tvar length = last-first+1;\n\t\t\tvar output = length > 0 ? this.writeCharToRegexpCharClass(first) : \"\";\n\t\t\toutput += (length>2 ? \"-\" : \"\");\n\t\t\tif (length>1) {\n\t\t\t\toutput += this.writeCharToRegexpCharClass(last);\n\t\t\t}\n\t\t\t\n\t\t\treturn output;\n\t\t}\n\t\t\n\t\t/**\n\t\t * Express an array of blocks(ranges) as a character class in a RegExp\n\t\t * by iteratively calling writeRangeToRegexpCharClass on each range, then concatenating the result\n\t\t * @see writeRangeToRegexpCharClass\n\t\t *\n\t\t * @param allRanges array of objects {first:..., last:..., size:...} to express as a character class \n\t\t * @return a string representing the character class, without the encapsulating [ ]\n\t\t */\n\t\tthis.writeBlocksToRegexpCharClass = function(allRanges) {\n\t\t\tvar currentCharClass = \"\";\n\t\t\tfor (var blockIndex = 0 ; blockIndex < allRanges.length ; ++blockIndex) {\n\t\t\t\tlet oneRange = allRanges[blockIndex];\n\t\t\t\tlet rangeString = this.writeRangeToRegexpCharClass(oneRange.first, oneRange.last);\n\t\t\t\t// Fix for issue #31 : if a token line begins with \"-\",\n\t\t\t\t// add it at the beginning of the character class instead of appending it\n\t\t\t\tif (oneRange.first==45) { // 45 is '-'\n\t\t\t\t\tcurrentCharClass=rangeString+currentCharClass;\n\t\t\t\t} else {\n\t\t\t\t\tcurrentCharClass+=rangeString;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn currentCharClass;\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * Returns true if the character requires a backlash as a prefix in the character class of the\n\t\t * RegExp to be interpreted as part of the expression\n\t\t * Character : \\ (used to escape others)\n\t\t * Character : ] (would close the character class otherwise)\n\t\t * @param ascii character code (ASCII / Unicode)\n\t\t * @return true if a backslash is needed, false otherwise\n\t\t */\n\t\tthis.needsEscapingInCharClass = function(ascii) {\n\t\t\treturn ascii==92||ascii==93;\n\t\t}\n\t\t\n\t}\n\t\n\tvar instance = null;\n\treturn new function() {\n\t\tthis.getInstance = function() {\n\t\t\tif (instance == null) {\n\t\t\t\tinstance = new constructor();\n\t\t\t\tinstance.constructor = null;\n\t\t\t}\n\t\t\t\n\t\t\treturn instance;\n\t\t}\n\t}\n})();\n\n\n// Node.js init\nif (typeof require !== 'undefined') {\n\tmodule.exports = StringHelper;\n}\n\n"
  },
  {
    "path": "tests/allTests.js",
    "content": "var testAudioContextCreate = require(\"./testAudioContextCreate\");\nvar testWebGLContextCreate = require(\"./testWebGLContextCreate\");\nvar testStringHelper = require(\"./testStringHelper\");\nvar testPackingConsistency = require(\"./testPackingConsistency\");\nvar testIssue0002_UnicodeSupport = require(\"./testIssue0002_UnicodeSupport\");\nvar testIssue0009_HashLoopVariable = require(\"./testIssue0009_HashLoopVariable\");\nvar testIssue0017_MultipleContexts = require(\"./testIssue0017_MultipleContexts\");\nvar testIssue0019_setInterval = require(\"./testIssue0019_setInterval\");\nvar testIssue0030_webGLContextCreate = require(\"./testIssue0030_webGLContextCreate\");\nvar testIssue0031_hyphenInRegex = require(\"./testIssue0031_hyphenInRegex\");\nvar testIssue0042_patternViewer = require(\"./testIssue0042_patternViewer\");\nvar testIssue0044_setIntervalArrowFunction = require(\"./testIssue0044_setIntervalArrowFunction\");\nvar testIssue0045_closingBracket = require(\"./testIssue0045_closingBracket\");\nvar testIssue0047_EscapeInCharClass = require(\"./testIssue0047_EscapeInCharClass\");\nvar testIssue0050_unicodeSurrogate = require(\"./testIssue0050_unicodeSurrogate\");\nvar testIssue0055_stringDelimiters = require(\"./testIssue0055_stringDelimiters\");\nvar testIssue0056_setIntervalDefaultParams = require(\"./testIssue0056_setIntervalDefaultParams\");\nvar testIssue0057_replacementInString = require(\"./testIssue0057_replacementInString\");\nvar testIssue0058_numberAsLoopVariable = require(\"./testIssue0058_numberAsLoopVariable\");\nvar testIssue0059_negatedRangeMerge = require(\"./testIssue0059_negatedRangeMerge\");\nvar testIssue0063_backtickFunctionParam = require(\"./testIssue0063_backtickFunctionParam\");\nvar testIssue0064_utf8EncodeURI = require(\"./testIssue0064_utf8EncodeURI\");\nvar testIssue0065_invalidEscapeSequence = require(\"./testIssue0065_invalidEscapeSequence\");\nvar testIssue0072_setIntervalNoInitCode = require(\"./testIssue0072_setIntervalNoInitCode\");\nvar testIssue0074_keepWhiteSpaceSeparator = require(\"./testIssue0074_keepWhiteSpaceSeparator\");\nvar testIssue0076_listVariablesInString = require(\"./testIssue0076_listVariablesInString\");\nvar testIssue0079_CandXMLComments = require(\"./testIssue0079_CandXMLComments\");\nvar testIssue0082_backticksInTemplateLiterals = require(\"./testIssue0082_backticksInTemplateLiterals\");\nvar testIssue0083_backslashToken = require(\"./testIssue0083_backslashToken\");\nvar testIssue0085_backslashSequenceLength = require(\"./testIssue0085_backslashSequenceLength\");\nvar testIssue0087_firstCharacterInPattern = require(\"./testIssue0087_firstCharacterInPattern\");\nvar testIssue0088_setIntervalAllocateVariable = require(\"./testIssue0088_setIntervalAllocateVariable\");\nvar testIssue0089_emptyThermalMapping = require(\"./testIssue0089_emptyThermalMapping\");\nvar testIssue0094_missingVariableBlock = require(\"./testIssue0094_missingVariableBlock\");\nvar testIssue0096_multiLineMinification = require(\"./testIssue0096_multiLineMinification\");\n\n// Execute all tests in sequence\n// Recommendation : put new tests at the very beginning while debugging\n// then push them down the list afterwards\ntestStringHelper();\ntestPackingConsistency();\ntestAudioContextCreate();\ntestWebGLContextCreate();\ntestIssue0002_UnicodeSupport();\ntestIssue0009_HashLoopVariable();\ntestIssue0017_MultipleContexts();\ntestIssue0019_setInterval();\ntestIssue0030_webGLContextCreate();\ntestIssue0031_hyphenInRegex();\ntestIssue0042_patternViewer();\ntestIssue0044_setIntervalArrowFunction();\ntestIssue0045_closingBracket();\ntestIssue0047_EscapeInCharClass();\ntestIssue0050_unicodeSurrogate();\ntestIssue0055_stringDelimiters();\ntestIssue0056_setIntervalDefaultParams();\ntestIssue0057_replacementInString();\ntestIssue0058_numberAsLoopVariable();\ntestIssue0059_negatedRangeMerge();\ntestIssue0063_backtickFunctionParam();\ntestIssue0064_utf8EncodeURI();\ntestIssue0065_invalidEscapeSequence();\ntestIssue0072_setIntervalNoInitCode();\ntestIssue0074_keepWhiteSpaceSeparator();\ntestIssue0076_listVariablesInString();\ntestIssue0079_CandXMLComments();\ntestIssue0082_backticksInTemplateLiterals();\ntestIssue0083_backslashToken();\ntestIssue0085_backslashSequenceLength();\ntestIssue0087_firstCharacterInPattern();\ntestIssue0088_setIntervalAllocateVariable();\ntestIssue0089_emptyThermalMapping();\ntestIssue0094_missingVariableBlock();\ntestIssue0096_multiLineMinification();\n"
  },
  {
    "path": "tests/documentMock.js",
    "content": "/**\n * Test mock for document (not present in node.js)\n * This instance simply stores the result in a string\n * \n * Used for tests involving HTML rendering, such as PatternViewer or ThermalViewer\n *\n */\nfunction DocumentMock() {\n\tthis.message = \"\";\n}\n\nDocumentMock.prototype =  {\n\tcreateElement : function(type) {\n\t\treturn new DivMock();\n\t},\n\t\n\tcreateTextNode : function(contents) {\n\t\tthis.message += \"[\" + contents + \"]\";\n\t\treturn contents;\n\t}\n\n}\n\n\nfunction DivMock() {\n}\n\nDivMock.prototype = {\n\tsetAttribute : function (a,b) {\n\t},\n\t\n\tappendChild : function(element) {\n\t}\n}\n\nmodule.exports = DocumentMock;"
  },
  {
    "path": "tests/runBenchmark.js",
    "content": "var regPack = require(\"../regPack\")\nvar fs = require('fs');\n\n\nvar sources = [\n\t{ fileName:\"2010 - Christmas Tree.js\", options:{contextVariableName : '' } },\n\t{ fileName:\"2012 - A rose is a rose.js\", options:{contextVariableName : 'a', wrapInSetInterval : true, timeVariableName : 'T' } },\n\t{ fileName:\"2012 - Autumn Evening.js\", options:{contextVariableName : 'a', wrapInSetInterval : true } },\n\t{ fileName:\"2012 - Mine(Love)craft.js\", options:{contextVariableName : 'a', wrapInSetInterval : true, timeVariableName : 'T' } },\n\t{ fileName:\"2013 - 3D City tour.js\", options:{contextVariableName : 'a' } },\n\t{ fileName:\"2013 - Color Factors.js\", options:{contextVariableName : 'a', wrapInSetInterval : true  } },\n\t{ fileName:\"2013 - Comanche.js\", options:{contextVariableName : 'a', wrapInSetInterval : true  } },\n\t{ fileName:\"2013 - Furbee.js\", options:{contextVariableName : 'a' } },\n\t{ fileName:\"2013 - Pointillism.js\", options:{contextVariableName : 'a', wrapInSetInterval : true, timeVariableName : 'J' } },\n\t{ fileName:\"2013 - Space Time Fracture.js\", options:{contextVariableName : 'a' } },\n\t{ fileName:\"2013 - StrangeCrystals II.js\", options:{contextVariableName : 'a', wrapInSetInterval : true, timeVariableName : 'H' } },\n\t{ fileName:\"2013 - Synth Sphere.js\", options:{contextVariableName : 'a', wrapInSetInterval : true } },\n\t{ fileName:\"2013 - Winter Wrap up.js\", options:{contextVariableName : 'a' } },\n\t{ fileName:\"2014 - Dragon Drop.js\", options:{} },\n\t{ fileName:\"2014 - Flappy Dragon Classic.js\", options:{} },\n\t{ fileName:\"2014 - Highway at night.js\", options:{ wrapInSetInterval : true, timeVariableName : 'I' } },\n\t{ fileName:\"2014 - Minecraft.js\", options:{ wrapInSetInterval : true } },\n\t{ fileName:\"2015 - Defender.js\", options:{ varsNotReassigned : 'abcV'} },\n\t{ fileName:\"2015 - Mysterious Monorail.js\", options:{} },\n\t{ fileName:\"2015 - Impossible road.js\", options:{} },\n\t{ fileName:\"2016 - Romanesco 2.0.js\", options:{ contextVariableName : 'g', contextType : 1} },\n\t{ fileName:\"2016 - Voxeling.js\", options:{ wrapInSetInterval : true, timeVariableName : 'e', varsNotReassigned : 'abc'} },\n\t{ fileName:\"2016 - Firewatch.js\", options:{ wrapInSetInterval : true, timeVariableName : 's'} },\n\t{ fileName:\"jscrush.js\", options: {contextVariableName : 'a', varsNotReassigned : 'b_' } }\n\t\n];\n\n\nvar defaultOptions = {\n\twithMath : false,\n\thash2DContext : true,\n\thashWebGLContext : true,\n\thashAudioContext : true,\n\tcontextVariableName : 'c',\n\tcontextType : parseInt(0),\n\treassignVars : true,\n\tvarsNotReassigned : 'abc',\n\tcrushGainFactor : parseFloat(1),\n\tcrushLengthFactor : parseFloat(0),\n\tcrushCopiesFactor : parseFloat(0),\n\tcrushTiebreakerFactor : parseInt(1),\n\twrapInSetInterval : false,\n\ttimeVariableName : \"\"\n};\n\nvar t0 = new Date();\n\nsources.forEach (function(referenceItem) {\n\tvar options = {};\n\tfor (key in defaultOptions) {\n\t\toptions[key] = defaultOptions[key];\n\t}\n\tfor (key in referenceItem.options)\n\t{\n\t\toptions[key] = referenceItem.options[key];\n\t}\n\n\tvar inputData = fs.readFileSync(\"../Benchmark/\"+referenceItem.fileName, { encoding:\"utf8\"});\n\tvar bestVal = regPack.cmdRegPack(inputData, options);\n\treferenceItem.finalSize = regPack.packer.getByteLength(bestVal);\n});\n\nvar tf = new Date();\n\n\nconsole.log(\" \");\nconsole.log(\"BENCHMARK RESULTS :\");\nconsole.log(\"Total time = \"+((tf-t0)/1000)+\" s\");\nconsole.log(\"-----------------\");\n\nsources.forEach (function(referenceItem) {\n\tconsole.log(referenceItem.fileName+\" : \" + referenceItem.finalSize);\n});\n"
  },
  {
    "path": "tests/testAudioContextCreate.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"AudioContext tests : start\");\n\ttestAssignInIfThenElse();\n\ttestCreateToDifferentVariablesFirst();\n\ttestCreateToDifferentVariablesSecond();\n\ttestAssignInConditionalExpression();\n\tconsole.log(\"AudioContext tests : done\");\n}\n\n\n/**\n * Creation of either AudioContext or webkitAudioContext in the then/else\n * statements of the same test, both stored in the same variable.\n *\n * Associated test file : audioContext_create1.js\n */\nfunction testAssignInIfThenElse() {\n\tvar input = fs.readFileSync(\"../TestCases/audioContext_create1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of AudioContext recognized\n\t// Audio-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 2);\n\tassert.equal(result[1].name, \" Audio\");\n}\n\n/**\n * Creation of either AudioContext or webkitAudioContext assigned to different variables.\n * AudioContext tested first.\n *\n * Associated test file : audioContext_create2.js\n */\nfunction testCreateToDifferentVariablesFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/audioContext_create2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of AudioContext recognized\n\t// Audio-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 2);\n\tassert.equal(result[1].name, \" Audio\");\n}\n\n/**\n * Creation of either AudioContext or webkitAudioContext assigned to different variables.\n * webkitAudioContext tested first.\n *\n * Associated test file : audioContext_create3.js\n */\nfunction testCreateToDifferentVariablesSecond() {\n\tvar input = fs.readFileSync(\"../TestCases/audioContext_create3.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of AudioContext recognized\n\t// Audio-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 2);\n\tassert.equal(result[1].name, \" Audio\");\n}\n\n/**\n * Creation of either AudioContext or webkitAudioContext in the same conditional expression\n *\n * Associated test file : audioContext_create4.js\n */\nfunction testAssignInConditionalExpression() {\n\tvar input = fs.readFileSync(\"../TestCases/audioContext_create4.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of AudioContext recognized\n\t// Audio-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 2);\n\tassert.equal(result[1].name, \" Audio\");\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0002_UnicodeSupport.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0002 - Unicode tests : start\");\n\ttestUnicodeSupport();\n\tconsole.log(\"Issue #0002 - Unicode tests : done\");\n}\n\n\n/**\n * Github issue #2 - Accept unicode characters\n * Make sure the Unicode characters are explicitely filtered out\n * by the RegExp in the negated char class\n *\n * Associated test file : gitHub#2-unicode.js\n */\nfunction testUnicodeSupport() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#2-unicode.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : internal check successful, \n\t// and the unicode characters are excluded from the token range\n\tassert.notEqual(result[0].result[2][1].indexOf(\"uffff]\"), -1);\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0009_HashLoopVariable.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0009 - Loop variable : start\");\n\ttestHashLoopVariable();\n\tconsole.log(\"Issue #0009 - Loop variable : done\");\n}\n\n\n/**\n * Github issue #9 - Hash loop variable\n * Make sure the variable chosen for the loop is not among the protected ones\n * Erroneous output spotted : \"for(c in c)...\"\n *\n * Associated test file : gitHub#9-hashloop.js\n */\nfunction testHashLoopVariable() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#9-hashloop.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : true,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : loop variable is not c \n\t// (candidates are c r z with equal score, tiebreaker is alphabetical order)\n\t// variable protection kicks in and c is ignored upon chhosing the name of the variable\n\tassert.notEqual(result[1].contents.substr(0,10), \"for(c in c\");\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0017_MultipleContexts.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0017 - Multiple contexts : start\");\n\ttestMultipleContexts();\n\tconsole.log(\"Issue #0017 - Multiple contexts : done\");\n}\n\n\n/**\n * Github issue #17 - Support for multiple contexts of the same type\n * Make sure all contexts of a given type are hashed at the same time\n * Erroneous behavior : hashing only the first context, but using renamed methods for all of them\n *\n * Associated test file : gitHub#17-multipleContexts.js\n */\nfunction testMultipleContexts() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#17-multipleContexts.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : true,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : methods for both c and cc are hashed\n\t// when performed on 2D methods (not 2D properties which are not concerned with the bug)\n\tassert.notEqual(result[1].contents.indexOf(\"c[i[0]+i[6]]=cc[i[0]+i[6]]=c[i]\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0019_setInterval.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0019 - setInterval() : start\");\n\ttestTimeVariableDeclaredAlone();\n\ttestTimeVariableDeclaredAtBegin1();\n\ttestTimeVariableDeclaredAtBegin2();\n\ttestTimeVariableDeclaredAtEnd1();\n\ttestTimeVariableDeclaredAtEnd2();\n\ttestTimeVariableDeclarationChainedFirst();\n\ttestTimeVariableDeclarationChainedSecond();\n\ttestTimeVariableDeclarationInArrayFirst();\n\ttestTimeVariableDeclarationInArraySecond();\n\ttestTimeVariableDeclarationInFunctionParameterFirst();\n\ttestTimeVariableDeclarationInFunctionParameterSecond();\n\ttestTimeVariableDeclarationInFunctionParameterLast();\n\tconsole.log(\"Issue #0019 - setInterval() : done\");\n}\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned alone, in the middle of other code (...;t=0;...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAlone.js\n */\nfunction testTimeVariableDeclaredAlone() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAlone.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned alone, at the beginning of the code, with a semicolon afterwards (t=0;...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAtBegin1.js\n */\nfunction testTimeVariableDeclaredAtBegin1() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAtBegin1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned alone, at the beginning of the code, with a comma afterwards (t=0,...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAtBegin2.js\n */\nfunction testTimeVariableDeclaredAtBegin2() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAtBegin2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned alone, at the beginning of the code, with a comma afterwards (t=0,...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAtBegin2.js\n */\nfunction testTimeVariableDeclaredAtBegin2() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAtBegin2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned at the end of the init code,\n * right before the setInterval(), with a semicolon before (;t=0;setInterval...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAtEnd1.js\n */\nfunction testTimeVariableDeclaredAtEnd1() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAtEnd1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and assigned at the end of the init code,\n * right before the setInterval(), with a comma before (,t=0;setInterval...)\n *\n * Associated test file : gitHub#19-setInterval_declarationAtEnd2.js\n */\nfunction testTimeVariableDeclaredAtEnd2() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAtEnd2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared in the same statement as another variable,\n * t coming first (t=u=0)\n *\n * Associated test file : gitHub#19-setInterval_declarationChained1.js\n */\nfunction testTimeVariableDeclarationChainedFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationChained1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared in the same statement as another variable,\n * t coming second (u=t=0)\n *\n * Associated test file : gitHub#19-setInterval_declarationChained2.js\n */\nfunction testTimeVariableDeclarationChainedSecond() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationChained2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared in an array definition, as first member (p=[t=0,...])\n *\n * Associated test file : gitHub#19-setInterval_declarationInArray1.js\n */\nfunction testTimeVariableDeclarationInArrayFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationInArray1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared in an array definition, as second member (p=[n,t=0,...])\n *\n * Associated test file : gitHub#19-setInterval_declarationInArray2.js\n */\nfunction testTimeVariableDeclarationInArraySecond() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationInArray2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and initialized as the first parameter passed to a function \n * f(t=0,...)\n * \n * Associated test file : gitHub#19-gitHub#19-setInterval_declarationInFunction1.js\n */\nfunction testTimeVariableDeclarationInFunctionParameterFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationInFunction1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and initialized as a parameter passed to a function \n * (not the first nor the last one) : f(..., t=0,...)\n * \n * Associated test file : gitHub#19-gitHub#19-setInterval_declarationInFunction2.js\n */\nfunction testTimeVariableDeclarationInFunctionParameterSecond() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationInFunction2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #19 - use setInterval() to evaluate the unpacked code\n * Time variable t declared and initialized as the last parameter passed to a function \n * f(..., t=0)\n * \n * Associated test file : gitHub#19-gitHub#19-setInterval_declarationInFunction3.js\n */\nfunction testTimeVariableDeclarationInFunctionParameterLast() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationInFunction3.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0030_webGLContextCreate.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0030 - WebGL create with ! : start\");\n\ttestCreateExclamationMarkInOptions();\n\tconsole.log(\"Issue #0030 - WebGL create with ! : done\");\n}\n\n\n/**\n * Github issue #30 - [description]\n * Creation of a GL context with \"!\" in the options string\n *\n * Associated test file : gitHub#30-webglContext_create_charset.js\n */\nfunction testCreateExclamationMarkInOptions() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#30-webglContext_create_charset.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0031_hyphenInRegex.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0031 - Hyphen in Regex : start\");\n\ttestDirectSingleHyphen();\n\ttestDirectHyphenBeginsBlock();\n\ttestDirectHyphenEndsBlock();\n\ttestNegatedSingleHyphen();\n\ttestNegatedHyphenBeginsBlock();\n\ttestNegatedHyphenEndBlock();\n\tconsole.log(\"Issue #0031 - Hyphen in Regex : done\");\n}\n\n\n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Packing generates a \"-\" in the regex in a block of 1 (single in the block)\n *\n * Associated test file : gitHub#31-direct-singleHyphen.js\n */\nfunction testDirectSingleHyphen() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#31-direct-singleHyphen.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\n\t\n}\n \n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Packing generates a \"-\" as the beginning of a block : [--0] (encompassing - . / 0)\n *\n * Associated test file : gitHub#31-direct-hyphenBeginsBlock.js\n */\nfunction testDirectHyphenBeginsBlock() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#31-direct-hyphenBeginsBlock.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t\n}\n\n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Packing generates a \"-\" as the end of a block : [*--] (encompassing * + , -)\n *\n * Associated test file : gitHub#31-direct-hyphenEndsBlock.js\n */\nfunction testDirectHyphenEndsBlock() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#31-direct-hyphenEndsBlock.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t\n}\n\n  \n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Negated char class contains a \"-\" as the beginning of a block : [^--z] (encompassing everything but - to z)\n *\n * Associated test file : gitHub#31-negated-singleHyphen.js\n */\nfunction testNegatedSingleHyphen() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#31-negated-singleHyphen.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\t\n}\n\n  \n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Negated char class contains a \"-\" as the beginning of a block : [^--z] (encompassing everything but - to z)\n *\n * Associated test file : gitHub#31-negated-hyphenBeginsBlock.js\n */\nfunction testNegatedHyphenBeginsBlock() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#31-negated-hyphenBeginsBlock.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\t\n}\n\n   \n/**\n * Github issue #31 - Single \"-\" character misinterpreted as range in RegExp\n * Negated char class contains a \"-\" as the end of a block : [^C--] (encompassing everything but C (a control character) to -)\n *\n * Associated test file : gitHub#31-negated-hyphenEndsBlock.js\n */\nfunction testNegatedHyphenEndBlock() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#31-negated-hyphenEndsBlock.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\t\n}\n\n \nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0042_patternViewer.js",
    "content": "var PatternViewer = require(\"../patternViewer\")\nvar fs = require(\"fs\");\nvar DocumentMock = require(\"./documentMock\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0042 - Pattern Viewer : start\");\n\ttestLastBlockBeforeEnd();\n\ttestLastBlockAtEnd();\n\tconsole.log(\"Issue #0042 - Pattern Viewer : done\");\n}\n\n\n/**\n * Github issue #42 - Visualization misses patterns that end at the last character\n * Single pattern not including the end of the block (regression test)\n */\nfunction testLastBlockBeforeEnd() {\n\tdocument = new DocumentMock();\n\tvar patternViewer = new PatternViewer();\n\tvar code = \"abcdefghijklmnopqrstuvwxyz\";\n\tvar matches = [ { token : \"A\", originalString : \"defg\" } ];\n\tvar result = patternViewer.render(code, matches);\n\tassert.equal(document.message, \"[abc][defg][hijklmnopqrstuvwxyz]\");\n\n}\n\n/**\n * Github issue #42 - Visualization misses patterns that end at the last character\n * Single pattern featuring the last characters of the block\n */\nfunction testLastBlockAtEnd() {\n\tdocument = new DocumentMock();\n\tvar patternViewer = new PatternViewer();\n\tvar code = \"abcdefghijklmnopqrstuvwxyz\";\n\tvar matches = [ { token : \"A\", originalString : \"wxyz\" } ];\n\tvar result = patternViewer.render(code, matches);\n\tassert.equal(document.message,\"[abcdefghijklmnopqrstuv][wxyz]\");\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0044_setIntervalArrowFunction.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0044 - setInterval() with arrow function : start\");\n\ttestMultipleParametersMultipleParams();\n\ttestSingleParameter();\n\ttestNoParameter();\n\n\tconsole.log(\"Issue #0044 - setInterval() with arrow function : done\");\n}\n\n/**\n * Github issue #44 - arrow function support for module \"refactor to setInterval()\"\n * Multiple parameters in arrow function, none is initialized there\n *\n * Associated test file : gitHub#44-setInterval_arrowFunctionMultiParam.js\n */\nfunction testMultipleParametersMultipleParams() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#44-setInterval_arrowFunctionMultiParam.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #44 - arrow function support for module \"refactor to setInterval()\"\n * Multiple parameters in arrow function, variable is initialized there\n *\n * Associated test file : gitHub#44-setInterval_arrowFunctionMultiParamInit.js\n */\nfunction testMultipleParametersWithInit() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#44-setInterval_arrowFunctionMultiParamInit.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n/**\n * Github issue #44 - arrow function support for module \"refactor to setInterval()\"\n * Single parameter function with no parenthesis\n *\n * Associated test file : gitHub#44-setInterval_arrowFunctionSingleParam.js\n */\nfunction testSingleParameter() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#44-setInterval_arrowFunctionSingleParam.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\n\n/**\n * Github issue #44 - arrow function support for module \"refactor to setInterval()\"\n * Function with no parameter\n *\n * Associated test file : gitHub#44-setInterval_arrowFunctionNoParam.js\n */\nfunction testNoParameter() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#44-setInterval_arrowFunctionNoParam.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"setInterval\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0045_closingBracket.js",
    "content": "var RegPack = require(\"../regPack\")\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0045 - ] in character class : start\");\n\ttestBracketBeginsBlock();\n\tconsole.log(\"Issue #0045 - ] in character class : done\");\n}\n\nfunction testBracketBeginsBlock() {\n \tvar input = \"=-=A==1234567=-=A-=A-1234567890AB=-A=-A=67890AB\";\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And it should not contain ] in the character class\n\tassert.equal(result[0].result[1][1].indexOf(\"[]\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0047_EscapeInCharClass.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0047 - \\\\ and ] in character class : start\");\n\ttestRangeSorting();\n\ttestEscapedCharacterWithoutReplacement();\n\ttestEscapedCharacterWithReplacement();\n\ttestEscapedCharacterReplacementFails();\n\tconsole.log(\"Issue #0047 - \\\\ and ] in character class : done\");\n}\n\n/**\n * GitHub issue #47 - Use \\ and ] (which need escaping) as tokens in packer\n * Range sorting consistency test (case met during development : last character had a value of NaN)\n * Ranges available for tokens are @ , B-E , L-P , ~\n *\n * Associated test file : gitHub#47-packer_rangesBeyond.js\n */\n\nfunction testRangeSorting() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#47-packer_rangesBeyond.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the blocks are L-P and B-E (used up to C), in that order\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[L-PBC]/\"), -1);\n}\n\n/**\n * GitHub issue #47 - Use \\ and ] (which need escaping) as tokens in packer\n * Test ranges starting or ending with escaped characters, with no replacement tokens (none left)\n *\n * Associated test file : gitHub#47-packer_to92NoReplace.js\n * Associated test file : gitHub#47-packer_to93NoReplace.js\n * Associated test file : gitHub#47-packer_from92NoReplace.js\n * Associated test file : gitHub#47-packer_from93NoReplace.js\n */\n\nfunction testEscapedCharacterWithoutReplacement() {\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\t\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#47-packer_to92NoReplace.js\", { encoding:\"utf8\"});\n\tvar result = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block ends in \\, no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[Y-\\\\\\\\]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_to93NoReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block ends in ], no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[Y-\\\\]]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_from92NoReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block begins with \\, no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[\\\\\\\\-`]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_from93NoReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block begins with ], no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[\\\\]-`]/\"), -1);\n\n}\n\n\n/**\n * GitHub issue #47 - Use \\ and ] (which need escaping) as tokens in packer\n * Test ranges starting or ending with escaped characters\n * with tokens left on the subsequent ranges to perform replacement\n *\n * Associated test file : gitHub#47-packer_to92AndReplace.js\n * Associated test file : gitHub#47-packer_to93AndReplace.js\n * Associated test file : gitHub#47-packer_from92AndReplace.js\n * Associated test file : gitHub#47-packer_from93AndReplace.js\n */\n\nfunction testEscapedCharacterWithReplacement() {\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\t\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#47-packer_to92AndReplace.js\", { encoding:\"utf8\"});\n\tvar result = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// Ranges Y-\\\\ 0-2, 2 unused, \\\\ gets replaced with 2\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[X-[0-2]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_to93AndReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block ends in ], no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[X-[0-2]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_from92AndReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block begins with \\, no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[0-3^-`]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_from93AndReplace.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block begins with ], no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[0-2^-a]/\"), -1);\n\n}\n\n/**\n * GitHub issue #47 - Use \\ and ] (which need escaping) as tokens in packer\n * Test ranges starting or ending with escaped characters\n * with tokens left on the subsequent ranges to perform replacement,\n * but not enough (1 where 2 are needed) -> no replacement is performed\n *\n * Associated test file : gitHub#47-packer_to93ReplaceFails.js\n * Associated test file : gitHub#47-packer_from92ReplaceFails.js\n */\nfunction testEscapedCharacterReplacementFails() {\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\t\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#47-packer_to93ReplaceFails.js\", { encoding:\"utf8\"});\n\tvar result = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// Ranges Y-\\\\ 0-2, 2 unused, \\\\ gets replaced with 2\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[X-\\\\]01]/\"), -1);\n\n\tinput = fs.readFileSync(\"../TestCases/gitHub#47-packer_from92ReplaceFails.js\", { encoding:\"utf8\"});\n\tresult = RegPack.packer.runPacker(input, options);\n\t// Expected result : the regular expression decodes correctly\n\tassert.notEqual(result[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\t// And the only block ends in ], no tokens are available for replacement\n\tassert.notEqual(result[0].result[1][1].indexOf(\"=/[\\\\]-`0-2]/\"), -1);\n}\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0050_unicodeSurrogate.js",
    "content": "﻿var RegPack = require(\"../regPack\")\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0050 - Unicode surrogate byte length : start\");\n\ttestByteLength();\n\ttestSurrogatePacking();\n\tconsole.log(\"Issue #0050 - Unicode surrogate byte length : done\");\n}\n\n/**\n * Github issue #50 - Support for characters in the astral plane\n * First, make sure that the astral characters (composed of two 16-bit codes,\n * first one in [0xD800, 0xDBFF] and second one in [0xDC00, DFFF]) are correctly read\n *\n */\nfunction testByteLength() {\n\t// standard ASCII\n \tvar input = \"0123456789abcdefghijklmnopqrstuvwxyz\";\n\tassert.equal(36, RegPack.packer.getByteLength(input));\n\t\n\t// 2-byte UTF-8\n\tinput = \"\";\n\tassert.equal(3, RegPack.packer.getByteLength(input));\n\t\n\t// 4-byte UTF-8 with surrogates\n\tinput = \"\\uD83D\\uDD25\\uD83D\\uDD25\\uD83D\\uDD25\";\n\tassert.equal(12, RegPack.packer.getByteLength(input));\n\t\n\tinput = \"🔥🔥🔥\";\n\tassert.equal(12, RegPack.packer.getByteLength(input));\n}\n\n\n/**\n * Github issue #50 - Support for characters in the astral plane\n * Then, check that the crusher does not attempt to break the input in between the two surrogate characters,\n * since a string starting with the second one would yield a malformed URI\n *\n */\nfunction testSurrogatePacking() {\n\t// 4-byte UTF-8 with surrogates\n\tinput = \"\\uD83D\\uDD25\\uD83D\\uDD25\\uD83D\\uDD25\\uD83D\\uDD25\\uD83D\\uDD25\\uD83D\\uDD25\";\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\"\n\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : no exception thrown before, internal check successful, \n\t// and the unicode characters are excluded from the token range\n\tassert(RegPack.packer.getByteLength(result[0].result[2][1]) > 0);\n\tassert.notEqual(result[0].result[2][1].indexOf(\"uffff]\"), -1);\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n}\n\n\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0055_stringDelimiters.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0055 - string delimiters as tokens : start\");\n\ttestUseBackticksForPackedString();\n\ttestBothQuotesUsedNoES6();\n\ttestQuoteAsToken();\n\ttestQuoteReplacement();\n\tconsole.log(\"Issue #0055 - string delimiters as tokens : done\");\n}\n\n/**\n * Github issue #55 - Harmonize strings delimiters inside the code, to free \" or ' as compression token\n * Input code uses both \" and ', test that packed string is defined with `\n *\n * Associated test file : gitHub#55-bothQuotesInUse.js\n */\nfunction testUseBackticksForPackedString() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#55-bothQuotesInUse.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : each stage delimits the packed string with `\n\tassert.equal(result[0].result[0][1].substr(0, 3), \"_=`\");\n\tassert.equal(result[0].result[1][1].substr(0, 7), \"for(_=`\");\n\tassert.equal(result[0].result[2][1].substr(0, 7), \"for(_=`\");\n}\n\n\n/**\n * Github issue #55 - Harmonize strings delimiters inside the code, to free \" or ' as compression token\n * Input code uses both \" and ', but ES6 (and thus backtick use) disabled\n * Same input file as testUseBackticksForPackedString()\n *\n * Associated test file : gitHub#55-bothQuotesInUse.js\n */\nfunction testBothQuotesUsedNoES6() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#55-bothQuotesInUse.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : false\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : each stage delimits the packed string with \" (` is not available)\n\tassert.equal(result[0].result[0][1].substr(0, 3), '_=\"');\n\tassert.equal(result[0].result[1][1].substr(0, 7), 'for(_=\"');\n\tassert.equal(result[0].result[2][1].substr(0, 7), 'for(_=\"');\n}\n\n\n/**\n * Github issue #55 - Harmonize strings delimiters inside the code, to free \" or ' as compression token\n * Input code uses neither ' nor \", and all other characters are used\n * Test that either ' or \" is used as string delimiter\n *\n * Associated test file : gitHub#55-quotesAsOnlyTokens.js\n */\nfunction testQuoteAsToken() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#55-quotesAsOnlyTokens.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : one of \" and ' is the delimiter, the other one the token\n\tvar delimiterCode = result[0].result[0][1].charCodeAt(2+result[0].result[0][1].indexOf(\"_=\"));\n\tvar tokenCode = result[0].result[0][1].charCodeAt(9+result[0].result[0][1].indexOf(\"for(i of\"));\n\tassert.equal(Math.min(delimiterCode, tokenCode), 34);\n\tassert.equal(Math.max(delimiterCode, tokenCode), 39);\n}\n\n/**\n * Github issue #55 - Harmonize strings delimiters inside the code, to free \" or ' as compression token\n * Input code has multiple copies of the same string, each time with different delimiters\n * Test that the delimiter is changed for some strings, so that one is freed for the packed code\n *\n * Associated test file : gitHub#55-sameStringInAllQuotes.js\n */\nfunction testQuoteReplacement() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#55-sameStringInAllQuotes.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : a quote is used only as delimiter\n\tvar delimiter = result[0].result[0][1][2+result[0].result[0][1].indexOf(\"_=\")];\n\tvar escapedDelimiter = \"\\\\\"+delimiter;\n\tfor (var stage=0; stage<3; ++stage) {\n\t\t// the only instances of the delimiter are around the string + around the tokens (for crusher stage)\n\t\tvar delimiterCount = result[0].result[stage][1].match(new RegExp(delimiter, \"g\")).length;\n\t\tassert.equal(delimiterCount, [4, 2, 2][stage]);\n\t\t// and the escaped delimiter is not present inside the string\n\t\tassert.equal(result[0].result[stage][1].indexOf(escapedDelimiter), -1);\n\t\t\n\t}\n\n}\n \n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0056_setIntervalDefaultParams.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0056 - setInterval() with default params : start\");\n\ttestNoAssignment();\n\ttestSingleVariableAssignment();\n\ttestOneAssignment();\n\ttestMultipleAssignments();\n\ttestComplexAssignments();\n\tconsole.log(\"Issue #0056 - setInterval() with default params : done\");\n}\n\n\n/**\n * Github issue #56 - default parameter values support for module \"refactor to setInterval()\"\n * No default value (regression test)\n *\n * Associated test file : gitHub#56-setInterval_arrowNoValue.js\n * Associated test file : gitHub#56-setInterval_standardNoValue.js\n */\nfunction testNoAssignment() {\n\tvar inputArrow = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_arrowNoValue.js\", { encoding:\"utf8\"});\n\tvar inputStandard = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_standardNoValue.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar resultArrow = RegPack.packer.preprocessor.preprocess(inputArrow, options);\n\tvar resultStandard = RegPack.packer.preprocessor.preprocess(inputStandard, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// Initialization of variable to default value is pushed to the main code\n\tassert.equal(resultArrow[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultArrow[0].wrappedInit, \"t=0\");\n\tassert.equal(resultArrow[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultArrow[0].contents.indexOf(\"x,y,z\"), -1); // make sure the variable list is discarded\n\tassert.equal(resultArrow[0].contents.indexOf(\"z;\"), -1); // make sure the last variable is discarded\n\n\tassert.equal(resultStandard[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultStandard[0].wrappedInit, \"t=0\");\n\tassert.equal(resultStandard[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultStandard[0].contents, resultArrow[0].contents);\t\n}\n\n\n/**\n * Github issue #56 - default parameter values support for module \"refactor to setInterval()\"\n * Single parameter with default value\n *\n * Associated test file : gitHub#56-setInterval_arrowSingleValue.js\n * Associated test file : gitHub#56-setInterval_standardSingleValue.js\n */\nfunction testSingleVariableAssignment() {\n\tvar inputArrow = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_arrowSingleValue.js\", { encoding:\"utf8\"});\n\tvar inputStandard = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_standardSingleValue.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar resultArrow = RegPack.packer.preprocessor.preprocess(inputArrow, options);\n\tvar resultStandard = RegPack.packer.preprocessor.preprocess(inputStandard, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// Initialization of variable to default value is pushed at the beginning of the main loop\n\tassert.equal(resultArrow[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultArrow[0].wrappedInit, \"t=0\");\n\tassert.equal(resultArrow[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.notEqual(resultArrow[0].contents.indexOf(\"x=0;if\"), -1); \n\n\tassert.equal(resultStandard[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultStandard[0].wrappedInit, \"t=0\");\n\tassert.equal(resultStandard[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultStandard[0].contents, resultArrow[0].contents);\t\n}\n\n/**\n * Github issue #56 - default parameter values support for module \"refactor to setInterval()\"\n * Multiple parameters, only one with default value\n *\n * Associated test file : gitHub#56-setInterval_arrowOneValue.js\n * Associated test file : gitHub#56-setInterval_standardOneValue.js\n */\nfunction testOneAssignment() {\n\tvar inputArrow = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_arrowOneValue.js\", { encoding:\"utf8\"});\n\tvar inputStandard = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_standardOneValue.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar resultArrow = RegPack.packer.preprocessor.preprocess(inputArrow, options);\n\tvar resultStandard = RegPack.packer.preprocessor.preprocess(inputStandard, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// Initialization of variable to default value is pushed at the beginning of the main loop\n\tassert.equal(resultArrow[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultArrow[0].wrappedInit, \"t=0\");\n\tassert.equal(resultArrow[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.notEqual(resultArrow[0].contents.indexOf(\"z=0;if\"), -1);\n\n\tassert.equal(resultStandard[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultStandard[0].wrappedInit, \"t=0\");\n\tassert.equal(resultStandard[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultStandard[0].contents, resultArrow[0].contents);\n\t\n}\n\n\n/**\n * Github issue #56 - default parameter values support for module \"refactor to setInterval()\"\n * Multiple parameters, several ones with default value\n *\n * Associated test file : gitHub#56-setInterval_arrowMultipleValues.js\n * Associated test file : gitHub#56-setInterval_standardMultipleValues.js\n */\nfunction testMultipleAssignments() {\n\tvar inputArrow = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_arrowMultipleValues.js\", { encoding:\"utf8\"});\n\tvar inputStandard = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_standardMultipleValues.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar resultArrow = RegPack.packer.preprocessor.preprocess(inputArrow, options);\n\tvar resultStandard = RegPack.packer.preprocessor.preprocess(inputStandard, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// Initialization of variable to default values is pushed at the beginning of the main loop\n\tassert.equal(resultArrow[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultArrow[0].wrappedInit, \"t=0\");\n\tassert.equal(resultArrow[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.notEqual(resultArrow[0].contents.indexOf(\"x=2,y=1,z=0;if\"), -1);\n\n\tassert.equal(resultStandard[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultStandard[0].wrappedInit, \"t=0\");\n\tassert.equal(resultStandard[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultStandard[0].contents, resultArrow[0].contents);\n}\n\n/**\n * Github issue #56 - default parameter values support for module \"refactor to setInterval()\"\n * Multiple parameters, including a complex assignment of several variables in a row (only the first one being a parameter)\n *\n * Associated test file : gitHub#56-setInterval_arrowComplexValues.js\n * Associated test file : gitHub#56-setInterval_standardComplexValues.js\n */\nfunction testComplexAssignments() {\n\tvar inputArrow = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_arrowComplexValues.js\", { encoding:\"utf8\"});\n\tvar inputStandard = fs.readFileSync(\"../TestCases/gitHub#56-setInterval_standardComplexValues.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar resultArrow = RegPack.packer.preprocessor.preprocess(inputArrow, options);\n\tvar resultStandard = RegPack.packer.preprocessor.preprocess(inputStandard, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// Initialization of variable to default values is pushed at the beginning of the main loop\n\tassert.equal(resultArrow[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultArrow[0].wrappedInit, \"t=0\");\n\tassert.equal(resultArrow[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.notEqual(resultArrow[0].contents.indexOf(\"w=t,x=y=z=0;if\"), -1);\n\n\tassert.equal(resultStandard[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(resultStandard[0].wrappedInit, \"t=0\");\n\tassert.equal(resultStandard[0].contents.indexOf(\"setInterval\"), -1);\n\tassert.equal(resultStandard[0].contents, resultArrow[0].contents);\n}\n\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0057_replacementInString.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0057 - Renaming of variable $ in string : start\");\n\ttestLettersOnly();\n\tconsole.log(\"Issue #0057 - Renaming of variable $ in string : done\");\n}\n\n\n/**\n * Github issue #57 - collisiion between ES6 string substitution expression ${...} and variable $\n * RegPack incorrectly replaces the $ in ${i}\n *\n * Associated test file : gitHub#57-templateLiteralOneVariable.js\n */\nfunction testLettersOnly() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#57-templateLiteralOneVariable.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : true,\n\t\thashWebGLContext : true,\n\t\thashAudioContext : true,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : true,\n\t\tvarsNotReassigned : \"\",\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : k is the most frequent loop index, and thus selected\n\tvar preprocessed = result[0].contents;\n\t// $ in ${ is not replaced\n\tassert.notEqual(preprocessed.indexOf(\"c.fillStyle=`hsl(256,25%,${\"), -1);\n\t// T in ${... T ...} is replaced\n\tassert.equal(preprocessed.indexOf(\"c.fillStyle=`hsl(256,25%,${Math.max(0,100-T)\"), -1);\n\t\n}\n \n \nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0058_numberAsLoopVariable.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0058 - Number as loop variable : start\");\n\ttestLettersOnly();\n\ttestProtectedAsMostFrequent();\n\ttestNumberAsMostFrequent();\n\tconsole.log(\"Issue #0058 - Number as loop variable : done\");\n}\n\n\n/**\n * Github issue #58 - Digit selected to use as a loop variable\n * Default case with letters only, selecting the most frequent\n *\n * Associated test file : gitHub#58-lettersOnly.js\n */\nfunction testLettersOnly() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#58-lettersOnly.js\", { encoding:\"utf8\"});\n\t\t\n\tvar protectedVars=[];\n\tfor (i=0;i<128;++i) {\n\t\tprotectedVars.push(i>96&&i<100); // true for a, b, c\n\t}\n\tvar result = RegPack.packer.preprocessor.getMostFrequentLoopVariable(input, protectedVars);\n\t\n\t// Expected result : k is the most frequent loop index, and thus selected\n\tassert.equal(result[0], \"k\");\n}\n \n/**\n * Github issue #58 - Digit selected to use as a loop variable\n * Case with letters only, where the most frequent variable is protected\n *\n * Associated test file : gitHub#58-lettersOnly.js\n */\nfunction testProtectedAsMostFrequent() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#58-lettersOnly.js\", { encoding:\"utf8\"});\n\t\n\tvar protectedVars=[];\n\tfor (i=0;i<128;++i) {\n\t\tprotectedVars.push((i>96&&i<100)||i==107); // true for a, b, c, k\n\t}\n\tvar result = RegPack.packer.preprocessor.getMostFrequentLoopVariable(input, protectedVars);\n\t\n\t// Expected result : k is the most frequent loop index, however it is protected\n\t// The function returns the second-most frequent which is p\n\tassert.equal(result[0], \"p\");\n\t\n}\n\n\n/**\n * Github issue #58 - Digit selected to use as a loop variable\n * Case where the most frequent \"letter\" is actually a digit\n *\n * Associated test file : gitHub#58-numberAsMostFrequent.js\n */\nfunction testNumberAsMostFrequent() {\n \tvar input = fs.readFileSync(\"../TestCases/gitHub#58-numberAsMostFrequent.js\", { encoding:\"utf8\"});\n\t\n\tvar protectedVars=[];\n\tfor (i=0;i<128;++i) {\n\t\tprotectedVars.push(i>96&&i<100); // true for a, b, c\n\t}\n\tvar result = RegPack.packer.preprocessor.getMostFrequentLoopVariable(input, protectedVars);\n\t\n\t// Expected result : 2 is the most frequent in expressions \"for(*\" \n\t// but not allowed as a variable name. \n\t// The function returns the most frequent legal variable name which is p\n\tassert.equal(result[0], \"p\");\n\t\n}\n \nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0059_negatedRangeMerge.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0059 - exhaustive merge in negated char class : start\");\n\ttestRangeMerge1vs3();\n\tconsole.log(\"Issue #0059 - exhaustive merge in negated char class : done\");\n}\n\n\n/**\n * GitHub issue #59 - when merging range to shorten the negated char class,\n * the original algorithm ends on a suboptimal solution\n * with 4 credits left, it chooses an option which gains 1 for 1 instead of one that gains 3 for 4\n *\n * Associated test file : gitHub#59-negatedRangeMerge-1vs3.js\n */\nfunction testRangeMerge1vs3() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#59-negatedRangeMerge-1vs3.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\thashAllObjects : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the range 0-3 is merged (gain 3, cost 4), leaving only } as a token\n\t// Incorrect result : the range } is merged (gain 1, cost 1), leaving 0-3 as tokens, 0 is used\n\tvar negatedCharClassModuleLog = output[0].result[2][2];\n\tvar rangePos = negatedCharClassModuleLog.indexOf(\", str = abcd\");\n\tvar tokenLog = negatedCharClassModuleLog.substr(rangePos-6, 6);\n\tassert.equal(tokenLog, \"125(})\");\n}\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0063_backtickFunctionParam.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0063 - getContext`param` : start\");\n\ttestBacktick2DContext();\n\ttestBacktickWebGLContext();\n\tconsole.log(\"Issue #0063 - getContext`param` : done\");\n}\n\n/**\n * Github issue #63 - Backtick use as wrapper for single function param : foo`x` instead of foo(\"x\")\n * Identify 2d context created by  getContext`2d`\n *\n * Associated test file : gitHub#63-backtick2DContext.js\n */\nfunction testBacktick2DContext() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#63-backtick2DContext.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : true,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : \"abcdg\",\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the 2d context is recognized\n\tassert.equal(result.length, 3);\n\tassert.notEqual(result[1].name.indexOf(\"2D\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"2D\"), -1);\n}\n\nfunction testBacktickWebGLContext() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#63-backtickWebGLContext.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : true,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : true,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : \"abcdg\",\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the WebGL context is recognized\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0064_utf8EncodeURI.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0064 - EncodeURI in UTF-8 : start\");\n\ttestEncodeURI();\n\tconsole.log(\"Issue #0064 - EncodeURI in UTF-8 : done\");\n}\n\n\n/**\n * Github issue #64 - Accept unicode characters\n * Make sure the Unicode characters are explicitely filtered out\n * by the RegExp in the negated char class\n *\n * Associated test file : gitHub#64-URIError.js\n */\nfunction testEncodeURI() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#64-URIError.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : internal check successful, \n\t// and the unicode characters are excluded from the token range\n\tassert.notEqual(result[0].result[2][1].indexOf(\"uffff]\"), -1);\n\tassert.notEqual(result[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0065_invalidEscapeSequence.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0065 - invalid escape sequence using \\\\ as token : start\");\n\ttestInvalidEscapeSequence();\n\ttestIncorrectReplacement();\n\tconsole.log(\"Issue #0065 - invalid escape sequence using \\\\ as token : done\");\n}\n\n\n\n/**\n * Assert that all backslashes \\ are doubled inside the input string\n * @packedCode string to check (packed by crusher or packer)\n */\nfunction assertAllBackslashesDoubled(packedCode) {\n\n\t// extract the packed string\n\tvar callFunction = \"eval(\";\n\tvar packedStringVarOffset = packedCode.lastIndexOf(callFunction);\n\tif (packedStringVarOffset == -1) {\n\t\tcallFunction = \"setInterval(\";\n\t\tpackedStringVarOffset= packedCode.lastIndexOf(callFunction);\n\t}\n\tif (packedStringVarOffset == -1) {\n\t\t// no unpacking routine found : code is not packed\n\t\tassert(false);\n\t}\n\tpackedStringVarOffset+=callFunction.length;\n\tvar packedStringVar = packedCode[packedStringVarOffset];\n\t\n\t// look for packed string : \n\tvar packedStringBegin = packedCode.indexOf(packedStringVar+\"=\");\n\tvar packedStringDelimiter = packedCode[packedStringBegin+2];\n\tvar packedStringEnd = packedCode.lastIndexOf(packedStringDelimiter);\n\t\n\tvar packedString = packedCode.substring(packedStringBegin+3, packedStringEnd);\n\n\n\tvar index = packedString.indexOf(\"\\\\\");\n\twhile (index>0) {\n\t\tassert(packedString.charCodeAt(index)==92);\n\t\tassert(packedString.charCodeAt(index+1)==92);\n\t\tindex = packedString.indexOf(\"\\\\\", index+2);\n\t}\n}\n\n\n\n/**\n * GitHub issue #65 - backslash \\ as token incorrectly escaped\n * Initial example filed with issue #65\n * Backslash interpreted as the beginning of an escape sequence\n *\n * Associated test file : gitHub#65-backslash_invalidEscapeSequence.js\n */\nfunction testInvalidEscapeSequence() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#65-backslash_invalidEscapeSequence.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly and matches the original code\n\tassert.notEqual(output[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\n\t// Expected result : the output from each stage has backslashes escaped\n\tassertAllBackslashesDoubled(output[0].result[0][1]);\n\tassertAllBackslashesDoubled(output[0].result[1][1]);\n\tassertAllBackslashesDoubled(output[0].result[2][1]);\n}\n\n/**\n * GitHub issue #65 - backslash \\ as token incorrectly escaped\n * Initial example filed with issue #73\n * Not all occurrences of backslash token are expanded \n *\n * Associated test file : gitHub#73-backslash_unexpandedToken.js\n */\nfunction testIncorrectReplacement() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#73-backslash_unexpandedToken.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(2),\n\t\tcrushLengthFactor : parseFloat(1),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly and matches the original code\n\tassert.notEqual(output[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\n\t// Expected result : the output from each stage has backslashes escaped\n\tassertAllBackslashesDoubled(output[0].result[0][1]);\n\tassertAllBackslashesDoubled(output[0].result[1][1]);\n\tassertAllBackslashesDoubled(output[0].result[2][1]);\n\n}\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0072_setIntervalNoInitCode.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Issue #0072 - setInterval() with empty initialization block : start\");\n\ttestInitCodeAtEnd();\n\ttestNoInitCode();\n\n\tconsole.log(\"Issue #0072 - setInterval() with empty initialization block : done\");\n}\n\n/**\n * Github issue #72 - do not generate an empty initialization block if there is no init code\n * Make sure the block is created if the init code is at the end\n *\n * Associated test file : inlined\n */\nfunction testInitCodeAtEnd() {\n\tvar input = \"t=0;setInterval(function(){t?play():init();t++},33);d=c.getImageData(0,0,640,320)\";\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// The initialization block is present\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.notEqual(result[0].contents.indexOf(\"if(!t){\"), -1);\n}\n\n\n/**\n * Github issue #72 - do not generate an empty initialization block if there is no init code\n * Make sure the block is not added if there is no init code at all - neither at the beginning nor at the end\n *\n * Associated test file : inlined\n */\nfunction testNoInitCode() {\n\tvar input = \"t=0;setInterval(function(){t?play():init();t++},33)\";\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : true,\n\t\t\ttimeVariableName : \"t\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : the encapsulation in setInterval is performed \n\t// The initialization block is missing\n\tassert.equal(result[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(result[0].wrappedInit, \"t=0\");\n\tassert.equal(result[0].contents.indexOf(\"if(!t){\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0074_keepWhiteSpaceSeparator.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0074 - Incorrect white space removal : start\");\n\ttestWhiteSpaceAsSeparator();\n\ttestMultipleWhiteSpaces();\n\tconsole.log(\"Issue #0074 - Incorrect white space removal : done\");\n}\n\n\n/**\n * Github issue #74 - Do not remove white spaces that are the only separator\n * Check that there is a proper separator\n * Erroneous behavior : create incorrect code with no separator between two instructions\n *\n * Associated test file : http://codepen.io/cantelope/pen/pRXaae\n */\nfunction testWhiteSpaceAsSeparator() {\n\tvar input = `function Q(x,y,z,S,D){T={};T.B=[];T.S=S;T.H=D;b={};b.M=x;b.N=y;b.A=z;b.K=b.G=PI-.01;b.l=25;b.C=b.M+sin(b.K)*sin(b.G)*25\nb.q=b.N+cos(b.G)*25;b.n=b.A+cos(b.K)*sin(b.G)*25;b.D=1;T.B.push(b);H(T.B[0],S,D,PI/4-cos(F/35)*PI/4.1);return T}`;\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the preprocessed text has not been modified, newline was kept\n\tassert.equal(result[0].contents, input);\n}\n\n\n\n/**\n * Github issue #74 - Do not remove white spaces that are the only separator\n * Check that several white spaces in a row are reduced to only one\n * Erroneous behavior : delete all white spaces, or keep more than one\n */\nfunction testMultipleWhiteSpaces() {\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar inputWithSpaces = \"let a=0; let  b=0; let   c=0\";\n\tvar result = RegPack.packer.runPacker(inputWithSpaces, options);\n\t\n\t// Expected result : only one space where relevant\n\tassert.equal(result[0].contents, \"let a=0;let b=0;let c=0\");\n\t\n\tvar inputWithTabs = \"let\\ta=0; let\\t\\tb=0; let\\t\\t\\tc=0\";\n\tresult = RegPack.packer.runPacker(inputWithTabs, options);\n\t\n\t// Expected result : only one tab where relevant\n\tassert.equal(result[0].contents, \"let\\ta=0;let\\tb=0;let\\tc=0\");\n}\n\nmodule.exports = runTests;\n"
  },
  {
    "path": "tests/testIssue0076_listVariablesInString.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0076 - text from string read as variables : start\");\n\ttestTemplateLiteralInterpretedAsDollarVariable();\n\tconsole.log(\"Issue #0076 - text from string read as variables : done\");\n}\n\n/**\n * Github issue #76 - Reassign variable names : still considering (but not replacing) text in strings\n * The $ defining the template literal ${..} was recognized as a variable\n * and assigned a replacement. The replacement was not performed inside strings, but this was still the loss of a variable.\n * \n * Associated test input : same as for #82\n */\nfunction testTemplateLiteralInterpretedAsDollarVariable() {\n\n\tvar input = \"p=c.getImageData(0,0,512,512);d=p.data;for(j=0;j<512;++j){c.fillStyle=`#${`200`,`300`,`fff`,`000`][j&3]}`;d[j*4]=0;d[j*4+1]=255;d[j*4+2]=j>>1;d[j*4+3]=0;c.beginPath();c.moveTo(0,j);c.lineTo(511,j);c.stroke();}\";\n\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : 'abc',\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : one template literal inside the string\n\tassert.equal(result[0].containedTemplateLiterals.length, 1);\n\t\n\t// Expected result : the variable $ is not renamed (and there is no variable $)\n\t// Buggy result : another name is reassigned to $\n\tassert.equal(result[0].log.indexOf(\"$ =>\"), -1);\n\t\n\t// Expected result : $ listed as keyword\n\tvar keywordsOffset = result[0].log.indexOf(\"in keywords only\");\n\tassert.notEqual(keywordsOffset, -1);\n\tassert.notEqual(result[0].log.indexOf(\"$\", keywordsOffset), -1);\n\t\n\t// Expected result : ${ still present in the string\n\tassert.notEqual(result[0].contents.indexOf(\"${\"), -1);\n}\n\n\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0079_CandXMLComments.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0079 - Minify C and XML comments : start\");\n\ttestCCommentMinification();\n\ttestCppCommentMinification();\n\ttestXMLCommentMinification();\n\tconsole.log(\"Issue #0079 - Minify C and XML comments : done\");\n}\n\n\n/**\n * Github issue #79 - Minify C and XML comments\n * Check that minification removed the C comments, both single and multiline\n * Erroneous behavior : keep the comment or remove only part of it\n *\n */\nfunction testCCommentMinification() {\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n    var inputWithSingleLineComment = \"if(i<0 /* || i>5 */)\";\n\tvar result = RegPack.packer.runPacker(inputWithSingleLineComment, options);\n\t// Expected result : the commented code is gone\n\tassert.equal(result[0].contents, \"if(i<0)\");\n    \n    var inputWithMultiLineComment = `/*\n* create an array and initialize it with fifty zeroes\n*/\nlet j=Array(50).fill(0));`\n    result = RegPack.packer.runPacker(inputWithMultiLineComment, options);\n\t// Expected result : the multiline comment is gone\n\tassert.equal(result[0].contents, \"let j=Array(50).fill(0));\");\n}\n\n\n/**\n * Github issue #79 - Minify C and XML comments\n * Check that minification removed the oneliner C++ comment, and only the comment\n * Erroneous behavior : remove the whole line, or keep the comment\n */\nfunction testCppCommentMinification() {\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n    var inputWithSingleLineComment = `var t=0;\nt=2; // change displayed value\nalert(t)`;\n\tvar result = RegPack.packer.runPacker(inputWithSingleLineComment, options);\n\t// Expected result : the comment is gone, but the remainder of the line is still there\n\tassert.equal(result[0].contents, \"var t=0;t=2;alert(t)\");\n}\n\n/**\n * Github issue #79 - Minify C and XML comments\n * Check that minification removed the XML comments, both single and multiline\n * Erroneous behavior : keep the comment or remove only part of it\n *\n */\nfunction testXMLCommentMinification() {\n\t\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n    var inputWithSingleLineComment = \"if(i<0 <!-- || i>5 -->)\";\n\tvar result = RegPack.packer.runPacker(inputWithSingleLineComment, options);\n\t// Expected result : the commented code is gone\n\tassert.equal(result[0].contents, \"if(i<0)\");\n    \n    var inputWithMultiLineComment = `<!--\n* create an array and initialize it with fifty zeroes\n-->\nlet j=Array(50).fill(0));`\n    result = RegPack.packer.runPacker(inputWithMultiLineComment, options);\n\t// Expected result : the multiline comment is gone\n\tassert.equal(result[0].contents, \"let j=Array(50).fill(0));\");\n}\n\n\nmodule.exports = runTests;\n"
  },
  {
    "path": "tests/testIssue0082_backticksInTemplateLiterals.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0082 - backticks in template literals : start\");\n\ttestUseBackticksForPackedString();\n\ttestBackticksEscapingInTemplateLiteral();\n\tconsole.log(\"Issue #0082 - backticks in template literals : done\");\n}\n\n\n\n/**\n * Github issue #82 - Backticks in template literal incorrectly read as end of string\n * An expression is defined with the construct ` ${variable+`other string`}`\n * The backticks inside the template literal ${} do not mark the end of the `-delimited string\n *\n */\nfunction testUseBackticksForPackedString() {\n\tvar input = \"p=c.getImageData(0,0,512,512);d=p.data;for(j=0;j<512;++j){c.fillStyle=`#${`200`,`300`,`fff`,`000`][j&3]}`;d[j*4]=0;d[j*4+1]=255;d[j*4+2]=j>>1;d[j*4+3]=0;c.beginPath();c.moveTo(0,j);c.lineTo(511,j);c.stroke();}\";\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t\n\t// Expected result : only one string is found within the input, not five\n\tassert.equal(result[0].containedStrings.length, 1);\n\t\n\t// Expected result : the variable j is renamed throughout the input, not a single copy remains\n\t// Buggy result : in \"[j&3]\", j is not renamed\n\tassert.equal(result[0].contents.indexOf(\"j\"), -1);\n}\n\n/**\n * Github issue #82 - Backticks in template literal incorrectly read as end of string\n * After fixing the main issue, the string is considered as a whole (good)\n * however the backticks inside are escaped \\` (bad)\n * \n * Since the string's delimiter do not change (to preserve the template literal)\n * escaping the backticks is not needed\n */\nfunction testBackticksEscapingInTemplateLiteral() {\n\t\n\tvar input = \"p=c.getImageData(0,0,512,512);d=p.data;for(j=0;j<512;++j){c.fillStyle=`#${`200`,`300`,`fff`,`000`][j&3]}`;d[j*4]=0;d[j*4+1]=255;d[j*4+2]=j>>1;d[j*4+3]=0;c.beginPath();c.moveTo(0,j);c.lineTo(511,j);c.stroke();}\";\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\n\t// Expected result : no escaped backticks inside the preprocessed string\n\tassert.equal(result[0].contents.indexOf(\"\\\\`\"), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0083_backslashToken.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0083 - backslash token makes output larger : start\");\n\ttestBackslashToken();\n\ttestCharacter9AloneInRange();\n\tconsole.log(\"Issue #0083 - backslash token makes output larger : done\");\n}\n\n\n\n/**\n * Github issue #83 - Don't use \"\\\" as a token if avoiding it makes the output smaller \n * \\ costs 2 whereas other tokens cost 1\n * The cost computation does not account for that extra cost\n *\n * Compare the result of packing the same string with the last character as only difference :\n *  - one ends with a closing bracket ) and thus has \\ as available token\n *  - the second one ends with \\ instead to remove it from token space\n *\n */\nfunction testBackslashToken() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#83-backslash_largerOutput.js\", { encoding:\"utf8\"});\n\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar resultWithBackspaceToken = RegPack.packer.runPacker(input+\")\", options);\n\tvar resultWithoutBackspaceToken = RegPack.packer.runPacker(input+\"\\\\\", options);\n\t\n\t// With the extra token available, compression should be at least as good (lower final size) as without it\n\tassert(resultWithBackspaceToken[0].result[1][0] <= resultWithoutBackspaceToken[0].result[1][0]);\n}\n\n/**\n * Github issue #83 - Don't use \"\\\" as a token if avoiding it makes the output smaller \n * \n * The first version of the code that passed all existing tests\n * caused a CR (char code 10) to be added to the character class in the benchmark Flappy Dragon.\n * \n * Make sure this artefact is gone on a similar sample that as a lone 9 (TAB) in the last token range\n */\nfunction testCharacter9AloneInRange() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#83-packer_9Alone.js\", { encoding:\"utf8\"});\n\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\t\t\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Make sure no CR has made it into the character class of the resulting string\n\tvar CRcharacter = String.fromCharCode(10);\n\tassert.equal(result[0].result[1][1].indexOf(CRcharacter), -1);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testIssue0085_backslashSequenceLength.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0085 - \\\\ not counted as length 2 : start\");\n\ttestBackslashSequence();\n\ttestFlappyDragon();\n\tconsole.log(\"Issue #0085 - \\\\ not counted as length 2 : done\");\n}\n\n\n/**\n * GitHub issue #85 - backslash \\ counted as length 1 instead of 2\n * resulting in \\\\\\\\ sequence reckoned not to be worth packing\n *\n * Example written for issue #85\n *\n * Associated test file : gitHub#85-backslashSequence.js\n */\nfunction testBackslashSequence() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#85-backslashSequence.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly and matches the original code\n\tassert.notEqual(output[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\n\t// Expected result : the sequence \\\\\\\\ is packed\n\tassert.notEqual(output[0].result[0][2].indexOf(\"str = \\\\\\\\\"), -1);\n\tassert.notEqual(output[0].result[1][2].indexOf(\"str = \\\\\\\\\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"str = \\\\\\\\\"), -1);\n\t\n}\n\n\n/**\n * GitHub issue #85 - backslash \\ counted as length 1 instead of 2\n * resulting in \\\\\\\\ sequence reckoned not to be worth packing\n *\n * Original example filed with issue #85\n *\n * Associated test file : gitHub#85-flappyDragon.js\n */\nfunction testFlappyDragon() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#85-flappyDragon.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the regular expression decodes correctly and matches the original code\n\tassert.notEqual(output[0].result[1][2].indexOf(\"Final check : passed\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"Final check : passed\"), -1);\n\n\t// Expected result : the sequence \\\\\\\\ is packed\n\tassert.notEqual(output[0].result[0][2].indexOf(\"str = \\\\\\\\\"), -1);\n\tassert.notEqual(output[0].result[1][2].indexOf(\"str = \\\\\\\\\"), -1);\n\tassert.notEqual(output[0].result[2][2].indexOf(\"str = \\\\\\\\\"), -1);\n\t\n}\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0087_firstCharacterInPattern.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0087 - first character part of a pattern : start\");\n\ttestFirstCharacterInCrusher();\n\tconsole.log(\"Issue #0087 - first character part of a pattern : done\");\n}\n\n\n/**\n * GitHub issue #87 - crusher ignores first character while searching for patterns\n * As a consequence, the pattern starts with the 2nd character instead\n *\n *\n */\nfunction testFirstCharacterInCrusher() {\n\tvar input = \"0123456789a0123456789b\";\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the first pattern is the string \"0123456789\", not \"123456789\"\n\tassert.equal(output[0].matchesLookup[0].originalString, \"0123456789\");\n\t\n}\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0088_setIntervalAllocateVariable.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0088 - variable allocation for setInterval() : start\");\n\ttestVariableAllocation();\n\tconsole.log(\"Issue #0088 - variable allocation for setInterval() : done\");\n}\n\n\n/**\n * GitHub issue #88 - allocateNewVariable() crashes after being given incorrect parameters\n * Crash happens when the module \"Refactor with setInterval()\" is called without being given a time variable\n * (meaning it has to allocate and declare its own variable)\n *\n * Reusing test case from issue #19, with different parameters\n *\n * Associated test file : gitHub#19-setInterval_declarationAlone.js\n */\nfunction testVariableAllocation() {\n\tvar input = fs.readFileSync(\"../TestCases/gitHub#19-setInterval_declarationAlone.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : false,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : true,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.preprocessor.preprocess(input, options);\n\n\t// Expected result : the encapsulation in setInterval is performed \n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\t// And a time variable is allocated (and it does not crash)\n\tassert.equal(output[0].interpreterCall, \"setInterval(_,33)\");\n\tassert.equal(output[0].wrappedInit.substr(1,2), \"=0\");\n\t\n}\n\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0089_emptyThermalMapping.js",
    "content": "var RegPack = require(\"../regPack\")\nvar ThermalViewer = require(\"../thermalViewer\")\nvar fs = require(\"fs\");\nvar DocumentMock = require(\"./documentMock\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0089 - thermal view with empty mapping : start\");\n\ttestEmptyThermalMapping();\n\tconsole.log(\"Issue #0089 - thermal view with empty mapping : done\");\n}\n\n\n/**\n * GitHub issue #89 - the development page showing all steps crashes with the mapping is empty\n * (no hashing, no reallocation)\n * \n * Crash happens in the ThermalViewer which tries to access the last element with the mapping\n * without performing bounds checking before\n *\n */\nfunction testEmptyThermalMapping() {\n\tvar input = \"0123456789abcdef\";\n\tvar thermalMapping = [];\n\t\n\tdocument = new DocumentMock();\n\tvar thermalViewer = new ThermalViewer();\n\t\n\tvar output = thermalViewer.render(input, thermalMapping);\n\n\t// Expected result : the thermalViewer returns without crashing\n\t// result is a <pre> with one text block\n\tassert.equal(document.message, \"[0123456789abcdef]\");\n}\n\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0094_missingVariableBlock.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0094 - missing last block in candidate variable names : start\");\n\ttestRenamingWithNoSpare();\n\tconsole.log(\"Issue #0094 - missing last block in candidate variable names : done\");\n}\n\n\n/**\n * GitHub issue #94 - reassignVariableNames() crashes after trying to read beyond array bounds\n * Crash happens when a candidate variable (usually z) is present in the last block of unused characters\n * and there are no spare variable names.\n */\nfunction testRenamingWithNoSpare() {\n\tvar input = '$=A=B=C=D=E=F=G=H=I=J=K=L=M=N=O=P=Q=R=S=T=U=V=W=X=Y=Z=_=a=b=c=d=e=f=g=h=i=j=k=l=m==n=o=p=q=r=s=t=u=v=w=x=y=z=\"ACFIMNPRSTVabcdefghiklmnopqrstuvwxy\"';\n\tvar options = {\n\t\twithMath : false,\n\t\thash2DContext : false,\n\t\thashWebGLContext : false,\n\t\thashAudioContext : false,\n\t\tcontextVariableName : false,\n\t\tcontextType : parseInt(0),\n\t\treassignVars : true,\n\t\tvarsNotReassigned : [],\n\t\tcrushGainFactor : parseFloat(1),\n\t\tcrushLengthFactor : parseFloat(0),\n\t\tcrushCopiesFactor : parseFloat(0),\n\t\tcrushTiebreakerFactor : parseInt(1),\n\t\twrapInSetInterval : false,\n\t\ttimeVariableName : \"\",\n\t\tuseES6 : true\n\t};\n\t\n\tvar output = RegPack.packer.preprocessor.preprocess(input, options);\n\n\t// Expected result : variable reassignment is performed\n\t// However, since there are no spares, each variable is reassigned to itself\n\t// z has a match (being z itself) and the operation does not crash\n\tassert.equal(output[0].contents, input);\n\t\n}\n\n\n\nmodule.exports = runTests"
  },
  {
    "path": "tests/testIssue0096_multiLineMinification.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\nfunction runTests() {\n\tconsole.log(\"Issue #0096 - Multi-line minification : start\");\n\ttestSingleLineMinification();\n\ttestMultiLineMinification();\n\tconsole.log(\"Issue #0096 - Multi-line minification : done\");\n}\n\n\n/**\n * Github issue #96 - Do not remove newlines or trailing blanks in template literals\n * Make sure that blanks are left inside strings. Test on single line string\n * Erroneous behavior : removing blanks inside the string\n */\nfunction testSingleLineMinification() {\n\tvar input = `var c=0, d=\"this is a single line with spaces\", e='this is another string with spaces', f=255`;\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the preprocessed text contains the original strings untouched, the spaces are still present\n\tassert.notEqual(result[0].contents.indexOf('=\"this is a single line with spaces\"'), -1);\n\tassert.notEqual(result[0].contents.indexOf(\"='this is another string with spaces'\"), -1);\n\t// make sure that other spaces are removed\n\tassert.equal(result[0].contents.substr(0, 10), \"var c=0,d=\");\n}\n\n/**\n * Github issue #96 - Do not remove newlines or trailing blanks in template literals\n * Make sure that blanks are left inside strings. Test on multiline strings defined as template literals (since ES6)\n * Erroneous behavior : removing blanks or newlines (CR) inside the string\n */\nfunction testMultiLineMinification() {\n\tvar input = `var c=0, d=\\`this is a multi line string\nused as an example.\\`, e=255` ;\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : \"c\",\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : ['a', 'b', 'c'],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the preprocessed text contains the original string untouched, the CR is still present\n\tassert.notEqual(result[0].contents.indexOf('this is a multi line string\\n'), -1);\n\t// make sure that other spaces are removed\n\tassert.equal(result[0].contents.substr(0, 10), \"var c=0,d=\");\n}\n\nmodule.exports = runTests;\n"
  },
  {
    "path": "tests/testPackingConsistency.js",
    "content": "var RegPack = require(\"../regPack\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"Packing consistency tests : start\");\n\ttestConsistency(\"../TestCases/gitHub#9-hashloop.js\");\n\ttestConsistency(\"../TestCases/gitHub#17-multipleContexts.js\");\n\ttestConsistency(\"../TestCases/gitHub#19-setInterval_declarationAlone.js\");\n\ttestConsistency(\"../TestCases/gitHub#30-webglContext_create_charset.js\");\n\ttestConsistency(\"../TestCases/gitHub#31-direct-hyphenBeginsBlock.js\");\n\ttestConsistency(\"../TestCases/gitHub#44-setInterval_arrowFunctionMultiParam.js\");\n\ttestConsistency(\"../TestCases/hash_using_length.js\");\n\tconsole.log(\"Packing consistency tests : done\");\n}\n\n/**\n * Unpacks a compressed string, independently of the wrapper (crusher or packer)\n */\nfunction unpack(packedCode) {\n\tvar callFunction = \"eval(\";\n\tvar packedStringVarOffset = packedCode.lastIndexOf(callFunction);\n\tif (packedStringVarOffset == -1) {\n\t\tcallFunction = \"setInterval(\";\n\t\tpackedStringVarOffset= packedCode.lastIndexOf(callFunction);\n\t}\n\tif (packedStringVarOffset == -1) {\n\t\t// no unpacking routine found : code is not packed\n\t\treturn packedCode;\n\t}\n\tpackedStringVarOffset+=callFunction.length;\n\tvar packedStringVar = packedCode[packedStringVarOffset];\n\t\n\t// look for packed string : \n\tvar packedStringBegin = packedCode.indexOf(packedStringVar+\"=\");\n\tvar packedStringDelimiter = packedCode[packedStringBegin+2];\n\tvar packedStringEnd = packedCode.lastIndexOf(packedStringDelimiter);\n\t\n\tvar packedString = packedCode.substring(packedStringBegin+3, packedStringEnd);\n\tvar originalString = packedString.replace(new RegExp(\"\\\\\\\\\"+packedStringDelimiter, \"g\"), packedStringDelimiter)\n\t\t\t\t\t\t\t\t\t .replace(/\\\\\\\\/g,'\\\\');\n\n\t\n\tvar beginRegPack = packedCode.indexOf(\"=/\", packedStringEnd);\n\tif (beginRegPack>0) {\t// RegPack marker identified\n\t\tvar endRegPack = packedCode.indexOf(\"/.exec\", beginRegPack);\n\t\tvar tokenString = packedCode.substring(beginRegPack+2, endRegPack);\n\t\tvar regToken = new RegExp(tokenString,\"\");\n\t\tfor(var token=\"\" ; token = regToken.exec(originalString) ; ) {\n\t\t\tvar k = originalString.split(token);\n\t\t\toriginalString = k.join(k.shift());\n\t\t}\n\t} else {\n\t\tvar tokensEnd = packedStringEnd;\n\t\tvar tokensBegin = packedCode.lastIndexOf(packedStringDelimiter, tokensEnd-1);\n\t\tpackedStringEnd = packedCode.lastIndexOf(packedStringDelimiter, tokensBegin-1);\n\t\t\n\t\tif (packedStringEnd > 0) {\t// JSCrush / FirstCrush marker identified\n\t\t\tpackedString = packedCode.substring(packedStringBegin+3, packedStringEnd);\n\t\t\toriginalString = packedString.replace(new RegExp(\"\\\\\\\\\"+packedStringDelimiter, \"g\"), packedStringDelimiter)\n\t\t\t\t\t\t\t\t\t\t .replace(/\\\\\\\\/g,'\\\\');\n\t\t\t\n\t\t\tvar tokenString= packedCode.substring(tokensBegin+1, tokensEnd);\n\t\t\tfor (var i in tokenString) {\n\t\t\t\tvar k = originalString.split(tokenString[i])\n\t\t\t\toriginalString = k.join(k.pop());\n\t\t\t}\n\t\t} \n\t}\n\treturn originalString;\n\n}\n\n/**\n * Improved assert that two strings match\n * In case of mismatch, show the first difference\n */\nfunction assertEqualStrings(actual, expected) {\n\tassert.equal(actual.length, expected.length);\n\tvar delta=0;\n\tfor (var i=0; i<actual.length; ++i) {\n\t\tif (actual[i] != expected[i]) {\n\t\t\tconsole.log (\"Difference at char \"+i+\", actual : \"+actual.substr(Math.max(0,i-3),10)+\"(\"+actual.charCodeAt(i)+\"), expected : \"+expected.substr(Math.max(0,i-3),10)+\"(\"+expected.charCodeAt(i)+\")\");\n\t\t\ti+=5;\n\t\t\tif (++delta>10) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tassert.equal(actual, expected);\n}\n\n\n/**\n * This test runs in an input through the crusher and packer stages,\n * then unpacks it and compares with the original. The two must match.\n */\nfunction testConsistency(inputFile) {\n\tvar input = fs.readFileSync(inputFile, { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : false,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : false,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(1),\n\t\t\tcrushLengthFactor : parseFloat(0),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\",\n\t\t\tuseES6 : true\n\t\t};\n\tvar output = RegPack.packer.runPacker(input, options);\n\t\n\t// Expected result : the compressed result at each stage matches the input\n\t// References to \"setInterval\" are removed from the main code and pushed to the encapsulating call\n\tassertEqualStrings(unpack(output[0].result[0][1]), output[0].contents);\n\t//console.log(\"Packed = \"+output[0].result[1][1]);\n\n\tassertEqualStrings(unpack(output[0].result[1][1]), output[0].contents);\n\tassertEqualStrings(unpack(output[0].result[2][1]), output[0].contents);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testStringHelper.js",
    "content": "var StringHelper = require(\"../stringHelper\")\nvar PackerData = require(\"../packerData\");\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"StringHelper tests : start\");\n\ttestGetByteLength();\n\ttestBase64();\n\ttestWriteRangeToRegexpCharClass();\n\ttestIsActualCodeAt();\n\tconsole.log(\"StringHelper tests : done\");\n}\n\n// basic implementation of btoa(), present in browser but not in node\nbtoa=function(input) {\n\tvar output=\"\";\n\tvar code=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\tfor (i=0; i<input.length; i+=3) {\n\t\toutput+=code[input.charCodeAt(i)>>2];\n\t\toutput+=code[((input.charCodeAt(i)&3)<<4)+(i+1<input.length ? input.charCodeAt(i+1)>>4 : 0)];\n\t\toutput+=i+1>=input.length ? \"=\" : code[((input.charCodeAt(i+1)&15)<<2) + (i+2<input.length ? input.charCodeAt(i+2)>>6 : 0)];\n\t\toutput+=i+2>=input.length ? \"=\" : code[input.charCodeAt(i+2)&63];\n\t}\n\treturn output;\n}\n\n// basic implementation of atob(), present in browser but not in node\natob=function(input) {\n\tvar output=\"\";\n\tvar code=\"=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\tfor (i=0; i<input.length; i+=4) {\n\t\tvar encoded0 = code.indexOf(input[i])-1;\n\t\tvar encoded1 = code.indexOf(input[i+1])-1;\n\t\tvar encoded2 = code.indexOf(input[i+2])-1;\n\t\tvar encoded3 = code.indexOf(input[i+3])-1;\n\t\toutput+=String.fromCharCode((encoded0<<2)+((encoded1&48)>>4));\n\t\toutput+=encoded2<0 ? \"\" : String.fromCharCode(((encoded1&15)<<4)+((encoded2&60)>>2));\n\t\toutput+=encoded3<0 ? \"\" : String.fromCharCode(((encoded2&3)<<6)+encoded3);\n\t}\n\treturn output;\n}\n/**\n * Unit test for StringHelper.getByteLength()\n */\nfunction testGetByteLength() {\n\tvar stringHelper = StringHelper.getInstance();\n\tassert.equal(stringHelper.getByteLength(\"e\"), 1);\n\tassert.equal(stringHelper.getByteLength(\"\u000f\"), 1);\n\tassert.equal(stringHelper.getByteLength(\"~\"), 1);\n\tassert.equal(stringHelper.getByteLength(\"\\x80\"), 2);\n\tassert.equal(stringHelper.getByteLength(\"\\xfc\"), 2);\n\tassert.equal(stringHelper.getByteLength(\"\\u0200\"), 2);\n\tassert.equal(stringHelper.getByteLength(\"\\u02ff\"), 2);\n\tassert.equal(stringHelper.getByteLength(\"\\u2000\"), 3);\n}\n\n/**\n * Unit test for StringHelper.unicodeToBase64()\n *               StringHelper.base64ToUnicode()\n */ \nfunction testBase64() {\n\tvar stringHelper = StringHelper.getInstance();\n\tassert.equal(stringHelper.unicodeToBase64(\"M\"), \"TQ==\");\n\tassert.equal(stringHelper.unicodeToBase64(\"Ma\"), \"TWE=\");\n\tassert.equal(stringHelper.unicodeToBase64(\"Man\"), \"TWFu\");\n\tassert.equal(stringHelper.unicodeToBase64(\"abc123!?$*&()'-=@~\"), \"YWJjMTIzIT8kKiYoKSctPUB+\");\n\tassert.equal(stringHelper.unicodeToBase64(\"This is the data, in the clear.\"), \"VGhpcyBpcyB0aGUgZGF0YSwgaW4gdGhlIGNsZWFyLg==\");\n\tassert.equal(stringHelper.unicodeToBase64(\"\\n\"), \"Cg==\");\n\tassert.equal(stringHelper.unicodeToBase64(\"\\u0227\"), \"yKc=\");\n\tassert.equal(stringHelper.unicodeToBase64(\"Base 64 \\u2014 Mozilla Developer Network\"), \"QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw==\");\n\tassert.equal(stringHelper.unicodeToBase64(\"\\u2713 \\xE0 la mode\"), \"4pyTIMOgIGxhIG1vZGU=\");\n\tassert.equal(stringHelper.unicodeToBase64(\"\\uD83D\\uDD25\"), \"8J+UpQ==\");\n\n\tassert.equal(stringHelper.base64ToUnicode(\"TQ==\"), \"M\");\n\tassert.equal(stringHelper.base64ToUnicode(\"TWE=\"), \"Ma\");\n\tassert.equal(stringHelper.base64ToUnicode(\"TWFu\"), \"Man\");\n\tassert.equal(stringHelper.base64ToUnicode(\"YWJjMTIzIT8kKiYoKSctPUB+\"), \"abc123!?$*&()'-=@~\");\n\tassert.equal(stringHelper.base64ToUnicode(\"VGhpcyBpcyB0aGUgZGF0YSwgaW4gdGhlIGNsZWFyLg==\"), \"This is the data, in the clear.\");\n\tassert.equal(stringHelper.base64ToUnicode(\"Cg==\"), \"\\n\");\n\tassert.equal(stringHelper.base64ToUnicode(\"yKc=\"), \"\\u0227\");\n\tassert.equal(stringHelper.base64ToUnicode(\"QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw==\"), \"Base 64 \\u2014 Mozilla Developer Network\");\n\tassert.equal(stringHelper.base64ToUnicode(\"4pyTIMOgIGxhIG1vZGU=\"), \"\\u2713 \\xE0 la mode\");\n\tassert.equal(stringHelper.base64ToUnicode(\"8J+UpQ==\"), \"\\uD83D\\uDD25\");\n\t\n}\n\n/**\n * Unit test for StringHelper.writeBlocksToRegexpCharClass()\n *               StringHelper.writeRangeToRegexpCharClass()\n *               StringHelper.writeCharToRegexpCharClass()\n *               StringHelper.needsEscapingInCharClass()\n */\nfunction testWriteRangeToRegexpCharClass () {\n\tvar stringHelper = StringHelper.getInstance();\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(48, 57), \"0-9\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(65, 90), \"A-Z\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(65, 66), \"AB\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(65, 65), \"A\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(45, 45), \"-\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(45, 48), \"--0\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(32, 35), \" -#\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(32, 34), ' -\"');\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(90, 95), \"Z-_\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(91, 95), \"[-_\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(90, 93), \"Z-\\\\]\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(90, 92), \"Z-\\\\\\\\\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(91, 92), \"[\\\\\\\\\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(92, 97), \"\\\\\\\\-a\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(93, 98), \"\\\\]-b\");\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(92, 93), \"\\\\\\\\\\\\]\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(126, 128), \"~-\\\\x80\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(130, 146), \"\\\\x82-\\\\x92\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(254, 256), \"\\\\xfe-\\\\u0100\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(512, 767), \"\\\\u0200-\\\\u02ff\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(110, 109), \"\");\t\n\tassert.equal (stringHelper.writeRangeToRegexpCharClass(35, 33), \"\");\t\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:48, last:57}]), \"0-9\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:65, last:90}]), \"A-Z\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:48, last:57}, {first:65, last:90}]), \"0-9A-Z\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:48, last:57}, {first:65, last:65}]), \"0-9A\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:48, last:57}, {first:65, last:65}, {first:67, last:67}]), \"0-9AC\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:48, last:57}, {first:65, last:65}, {first:45, last:45}]), \"-0-9A\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:91, last:95}, {first:45, last:45}, {first:65, last:90}]), \"-[-_A-Z\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:91, last:95}, {first:45, last:46}, {first:65, last:90}]), \"-.[-_A-Z\");\n\tassert.equal (stringHelper.writeBlocksToRegexpCharClass([{first:91, last:95}, {first:45, last:48}, {first:65, last:90}]), \"--0[-_A-Z\");\n}\n\n/**\n * Unit test for StringHelper.isActualCodeAt()\n */\nfunction testIsActualCodeAt() {\n\tvar packerData = new PackerData();\n\tvar stringHelper = StringHelper.getInstance();\n\n\t// empty string analysis : everything is code\n\tassert.equal (stringHelper.isActualCodeAt(0, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(20, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(400, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(8000, packerData), true);\n\t\n\t// two strings, the first one contains a template literal\n\tpackerData.containedStrings = [ {begin : 20, end : 100}, {begin : 200, end : 300} ];\n\tpackerData.containedTemplateLiterals = [ {begin : 40, end : 70} ];\n\t\n\tassert.equal (stringHelper.isActualCodeAt(0, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(19, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(21, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(39, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(41, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(69, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(71, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(99, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(101, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(199, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(201, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(299, packerData), false);\n\tassert.equal (stringHelper.isActualCodeAt(301, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(400, packerData), true);\n\tassert.equal (stringHelper.isActualCodeAt(8000, packerData), true);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "tests/testWebGLContextCreate.js",
    "content": "var RegPack = require(\"../regPack\")\nvar fs = require(\"fs\");\nvar assert = require(\"assert\");\n\n\nfunction runTests() {\n\tconsole.log(\"WebGL Context tests : start\");\n\ttestCreateExperimentalWebGL();\n\ttestCreateWebGL();\n\ttestCreateBothFirst();\n\ttestCreateBothSecond();\n\ttestCreateOptionsFirst();\n\ttestCreateOptionsSecond();\n\ttestHashCollisions();\n\tconsole.log(\"WebGL Context tests : done\");\n}\n\n\n/**\n * Creation of a GL context as \"experimental-webgl\" only\n *\n * Associated test file : webglContext_create1.js\n */\nfunction testCreateExperimentalWebGL() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create1.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n\n/**\n * Creation of a GL context as \"webgl\" only\n *\n * Associated test file : webglContext_create2.js\n */\nfunction testCreateWebGL() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create2.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n/**\n * Creation of a GL context as either \"webgl\" or \"experimental-webgl\" in the same conditional expression\n * (experimental-webgl mentioned first)\n *\n * Associated test file : webglContext_create3.js\n */\nfunction testCreateBothFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create3.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n\n/**\n * Creation of a GL context as either \"webgl\" or \"experimental-webgl\" in the same conditional expression\n * (webgl mentioned first)\n *\n * Associated test file : webglContext_create4.js\n */\nfunction testCreateBothSecond() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create4.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n/**\n * Creation of a GL context as either \"webgl\" or \"experimental-webgl\" in the same conditional expression\n * along with options stored in a variable defined earlier.\n *\n * Associated test file : webglContext_create5.js\n */\nfunction testCreateOptionsFirst() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create5.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n\n/**\n * Creation of a GL context as either \"webgl\" or \"experimental-webgl\" in the same conditional expression\n * along with options defined in the same line\n *\n * Associated test file : webglContext_create6.js\n */\nfunction testCreateOptionsSecond() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_create6.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : creation of WebGL Context recognized\n\t// WebGL-hashed environment added to input lines\n\t\n\tassert.equal(result.length, 4);\n\tassert.notEqual(result[1].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[2].name.indexOf(\"WebGL\"), -1);\n\tassert.notEqual(result[3].name.indexOf(\"WebGL\"), -1);\n}\n\n\n/**\n * Detection of hash collisions between two methods, one containing entierly the name of the other\n * Example tested with enable() and enableVertexAttribArray()\n *\n * Associated test file : webglContext_substringHash.js\n */\nfunction testHashCollisions() {\n\tvar input = fs.readFileSync(\"../TestCases/webglContext_substringHash.js\", { encoding:\"utf8\"});\n\tvar options = {\n\t\t\twithMath : false,\n\t\t\thash2DContext : false,\n\t\t\thashWebGLContext : true,\n\t\t\thashAudioContext : false,\n\t\t\tcontextVariableName : false,\n\t\t\tcontextType : parseInt(0),\n\t\t\treassignVars : true,\n\t\t\tvarsNotReassigned : [],\n\t\t\tcrushGainFactor : parseFloat(2),\n\t\t\tcrushLengthFactor : parseFloat(1),\n\t\t\tcrushCopiesFactor : parseFloat(0),\n\t\t\tcrushTiebreakerFactor : parseInt(1),\n\t\t\twrapInSetInterval : false,\n\t\t\ttimeVariableName : \"\"\n\t\t};\n\tvar result = RegPack.packer.preprocessor.preprocess(input, options);\n\t\n\t// Expected result : hashed value for both enable() and enableVertexAttribArray() is different\n\t// upon hashing methods\n\tvar hash1Match = result[1].contents.match(/gl\\.(\\w*)\\(gl.DEPTH_TEST/);\n\tvar hash1Value = hash1Match[1];\n\tvar hash2Match = result[1].contents.match(/gl\\.(\\w*)\\(\\);/);\n\tvar hash2Value = hash2Match[1];\n\tassert.notEqual(hash1Value, hash2Value);\n\t\n\t// and same result upon hashing properties\n\thash1Match = result[3].contents.match(/gl\\[gl\\.(\\w*)\\]\\(gl\\[gl.(\\w*)\\]\\);/);\n\thash1Value = hash1Match[1];\n\thash2Match = result[3].contents.match(/gl\\[gl\\.(\\w*)\\]\\(\\);/);\n\thash2Value = hash2Match[1];\n\tassert.notEqual(hash1Value, hash2Value);\n}\n\nmodule.exports = runTests;"
  },
  {
    "path": "thermalViewer.js",
    "content": "/**\n * @constructor\n * The ThermalViewer class renders the original code into a DHTML view,\n * using background color to show the final (packed) size of each character\n *\n *\n * @param name The name of the branch, summing the operations performed\n * @param dataString The input string to pack\n */\nfunction ThermalViewer ()\n{\n}\n\n\nThermalViewer.prototype = {\n\n\t/**\n\t * Produces an HTML render of the patterns used by the packer.\n\t * The rendered <div> is not added to the page HTML.\n\t * \n\t * @param unpackedCode The original unpacked code (before preprocessing)\n\t * @param thermalMap Array listing all the mappings for the successive replacements\n\t * @return A <div> object showing the varying compression rates in the code\n\t *\n\t */\n\trender : function(unpackedCode, thermalMap) {\n\t\tvar output = document.createElement(\"pre\");\n\t\toutput.setAttribute(\"class\",\"topLevel\");\n\t\t\n\t\t// #89 : if the mapping is empty, assume identity\n\t\t// (no compression / replacement, every character stored as is)\n\t\tvar finalSize = unpackedCode.length;\n\t\tif (thermalMap.length > 0) {\n\t\t\tfinalSize = thermalMap[thermalMap.length-1][0].outLength;\n\t\t}\n\n\t\t// transform the successive mappings to a heatmap\n\t\t// start from the final size : 8 bits for each character of the output\n\t\tvar currentHeatMap = new Array(finalSize).fill(8);\n\t\t\n\t\tfor (let stageIndex=thermalMap.length-1; stageIndex>=0; --stageIndex) {\n\t\t\tlet currentMapping = thermalMap[stageIndex];\n\t\t\tvar nextHeatMap = currentHeatMap;\n\t\t\tif (currentMapping[0].complete) {\t\n\t\t\t\t// the transform covers every byte of the string, hence the map is recomputer from scratch\n\t\t\t\t// as opposed to extra chapters that are only added to the current cost map\n\t\t\t\tnextHeatMap = new Array(currentMapping[0].inLength).fill(0);\n\t\t\t}\n\t\t\tfor (let blockIndex=1; blockIndex<currentMapping.length; ++blockIndex) {\n\t\t\t\tlet block=currentMapping[blockIndex];\n\t\t\t\tif (block.rangeIn.length==2 && block.rangeIn[1] == block.rangeOut[1]) {\n\t\t\t\t\t// same-length mapping : transfer character cost individually\n\t\t\t\t\tfor (let offset=0; offset<block.rangeIn[1]; ++offset) {\n\t\t\t\t\t\tnextHeatMap[block.rangeIn[0]+offset] += currentHeatMap[block.rangeOut[0]+offset];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// even-out the cost on all blocks\n\t\t\t\t\tvar totalCost = 0;\n\t\t\t\t\tif (block.chapter == 0) {\t// blocks inside the main code : compound costs\n\t\t\t\t\t\tfor (let offset=0; offset<block.rangeOut[1]; ++offset) {\n\t\t\t\t\t\t\ttotalCost += currentHeatMap[block.rangeOut[0]+offset];\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\t// block in the unpacking routine : each character is exactly 8 bits\n\t\t\t\t\t\ttotalCost = 8*block.rangeOut[1];\n\t\t\t\t\t}\n\t\t\t\t\tvar totalLength = 0;\n\t\t\t\t\tfor (let rangeIndex=1; rangeIndex<block.rangeIn.length; rangeIndex+=2) {\n\t\t\t\t\t\ttotalLength+=block.rangeIn[rangeIndex];\n\t\t\t\t\t}\n\t\t\t\t\tvar byteCost = totalCost / totalLength;\n\t\t\t\t\tfor (let rangeIndex=0; rangeIndex<block.rangeIn.length; rangeIndex+=2) {\n\t\t\t\t\t\tfor (let offset=0; offset<block.rangeIn[rangeIndex+1]; ++offset) {\n\t\t\t\t\t\t\tnextHeatMap[block.rangeIn[rangeIndex]+offset] += byteCost;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentHeatMap = nextHeatMap;\n\t\t}\n\t\t\n\t\t\n\n\t\tvar currentColorValue = -1;\n\t\tvar startOffset = 0;\n\t\tfor (var offset=0; offset<=unpackedCode.length; ++offset) {\n\t\t\n\t\t\tvar color = Math.floor(currentHeatMap[offset]);\n\t\t\tif (color != currentColorValue) {\n\t\t\t\tif (offset > 0) {\n\t\t\t\t\tthis.addTextBlock(output, currentColorValue, unpackedCode.substring(startOffset, offset));\n\t\t\t\t}\n\t\t\t\tstartOffset = offset;\n\t\t\t\tcurrentColorValue = color;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (startOffset<unpackedCode.length) {\n\t\t\tthis.addTextBlock(output, currentColorValue, unpackedCode.substring(startOffset));\n\t\t}\n\n\t\treturn output;\n\t},\n\t\n\t/**\n\t * Internal method : adds a block of text to the output node.\n\t * A block of text is a group of characters of the same color.\n\t * @param parentNode the <div> or <pre> where the new block will be appended as a child\n\t * @param colorClass background color index, integer [0-10] \n\t * @param text the text to display\n\t */\n\taddTextBlock : function(parentNode, colorClass, text) {\n\t\tvar newSpan = document.createElement(\"span\");\n\t\tnewSpan.appendChild(document.createTextNode(text));\n\t\tnewSpan.setAttribute(\"class\", \"thermal\"+Math.min(10, colorClass));\n\t\tparentNode.appendChild(newSpan);\n\t}\n}\n\n\n// Node.js exports (for non-regression tests only)\nif (typeof require !== 'undefined') {\n\tmodule.exports = ThermalViewer;\n}"
  }
]