function htmlJavaScriptEncode(sString) {
  var sEncoded = '';
  for (var i = 0; i < sString.length; i++) {
    var iChar = sString.charCodeAt(i);
    if (iChar < 0x10) {
      sEncoded += '\\x0' + iChar.toString(16);
    } else if (iChar < 0x20 || iChar > 0x7E) {
      sEncoded += '\\x' + iChar.toString(16);
    } else {
      var asMap = {
        '\"': '\\"',
        '\\': '\\\\',
        '&': '\x26',
        '<': '\x3C',
      }
      sEncoded += asMap[sString[i]] || sString[i];
    }
  }
  return sEncoded;
}
var iOffsetSprites = 0, iOffsetSpriteMaps = 0;
function generateCode() {
  var sMain = (fMain+'').replace(/^function\s+\w+\s*\(\s*\)\s*\{|\s+|\/\/.*?\r\n|\/\*.*?\*\/|\}$/g, '');
  var sSprite = (fSprite+'').replace(/^function\s+\w+|\s+|\/\/.*?\r\n|\/\*.*?\*\/$/g, '');
  var sTemplate = 'f=function' + htmlJavaScriptEncode(sSprite) + ';' +
                  htmlJavaScriptEncode(sMain);
  return sTemplate.replace(/iOffsetSprites/g, iOffsetSprites).replace(/iOffsetSpriteMaps/g, iOffsetSpriteMaps);
}

function generateStringArrayCode(asData, sOtherData) {
  var asEncodedData = [];
  for (var i = 0; i < asData.length; i++) {
    asEncodedData[i] = '"' + htmlJavaScriptEncode(asData[i]) + '"';
  }
  return '[' + asEncodedData.join(',') + ']';
  var sData = asData.join('') + (sOtherData || '');
  var sChars = '0123456789abcdefghijklmnopqrstuvwxyz.,+-()}{[]:?*';
  for (var iChar in sChars) {
    var sChar = sChars[iChar];
    if (sData.indexOf(sChar) < 0) {
      var xChar = iChar == sChar ? iChar : '"' + sChar + '"';
      return '"' + htmlJavaScriptEncode(asData.join(sChar)) + '".split(' + xChar + ')';
    }
  }
  throw 'assert:' + sData;
}

function storeMultiArray(asData, iData) {
  for (var iOffset = 0x0; iOffset < 0x7F; iOffset++) {
    if (!asData[iOffset]) asData[iOffset] = '';
    asData[iOffset] += htmlJavaScriptEncode(String.fromCharCode(iData + iOffset));
  }
}
function chooseMostEffectiveOffset(axData, fDataEncoder) {
  var sCode = generateCode();
  var rCharsInCode = new RegExp('[' + sCode.split('').sort().join('').replace(/(.)\1*/g, '\\$1') + ']', 'g');
  var iOffset = null, iCodeSize0, iCodeSize0NewChars;
  for (var i = axData.length - 1; i >= 0; i--) {
    try {
      var sCode = fDataEncoder(axData[i]);
    } catch (e) {
      continue;
    }
    var iCodeSize1 = sCode.length,
        iCodeSize1NewChars = sCode.replace(rCharsInCode, '').length;
    if (iOffset == null ||
        iCodeSize1 < iCodeSize0 ||
        (iCodeSize1 == iCodeSize0 && iCodeSize1NewChars < iCodeSize0NewChars) ||
        (iCodeSize1 == iCodeSize0 && iCodeSize1NewChars == iCodeSize0NewChars && i < iOffset)) {
      iOffset = i;
      iCodeSize0 = iCodeSize1;
      iCodeSize0NewChars = iCodeSize1NewChars;
    }
  }
  return iOffset;
}

var aoSprites = [
  poptart2 = {
    sprite: [
      "33111111111111133",
      "31111112112111113",
      "11211111111111111",
      "11111111111111211",
      "11111111111111111",
      "11111121111111111",
      "11111111111111111",
      "11121111111111111",
      "11111112111111111",
      "12111111111111111",
      "11111111111111111",
      "11111211111111111",
      "31211111111111111",
      "33111111111111111" ],
    palette: 'f39fc9',
  }, cheek = {
    sprite: [
      "22",
      "22",
    ],
    palette: 'f99',
  }, star_eye0 = {
    sprite: [
      "1111",
      "1111",
      "1111",
      "1112", ],
    palette: 'fff',
  }, star1 = {
    sprite: [
      "11111",
      "11111",
      "11121",
      "11212",
      "11121", ],
    palette: 'fff',
  }, star2 = {
    sprite: [
      "111111",
      "111211",
      "111211",
      "122122",
      "111211",
      "111211", ],
    palette: 'fff',
  }, star3 = {
    sprite: [
      "1112111",
      "1112111",
      "1111111",
      "2212122",
      "1111111",
      "1112111",
      "1112111", ],
    palette: 'fff',
  }, star4 = {
    sprite: [
      "1112111",
      "1211121",
      "1111111",
      "2111112",
      "1111111",
      "1211121",
      "1112111", ],
    palette: 'fff',
  }, star5 = {
    sprite: [
      "1112111",
      "1111111",
      "1111111",
      "2111112",
      "1111111",
      "1111111",
      "1112111", ],
    palette: 'fff',
  }, poptart1 = {
    sprite: [
      "1333333333333333331",
      "3111111111111111113",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "1111111111111111111",
      "3111111111111111111",
      "2111111111111111112" ],
    palette: '999000',
  }, tail0 = {
    sprite: [
      "333311",
      "322331",
      "332233",
      "133222",
      "113322",
      "111333",
      "111113" ],
    palette: '999000',
  }, tail1 = {
    sprite: [
      "133111",
      "322311",
      "322333",
      "132222",
      "113322",
      "111133" ],
    palette: '999000',
  }, tail2 = {
    sprite: [
      "111113",
      "113333",
      "332222",
      "322233",
      "133331" ],
    palette: '999000',
  }, tail3 = {
    sprite: [
      "111133",
      "113322",
      "132222",
      "322333",
      "322311",
      "133111" ],
    palette: '999000',
  }, tail4 = {
    sprite: [
      "1333311",
      "3222333",
      "3322223",
      "1133332",
      "1111133" ],
    palette: '999000',
  }, paws0 = {
    sprite: [
      "13",
      "322" ],
    palette: '999000',
  }, paws1 = {
    sprite: [
      "32" ],
    palette: '999000',
  }, paws3 = {
    sprite: [
      "13",
      "33" ],
    palette: '999000',
  }, paws4 = {
    sprite: [
      "32233",
      "33331" ],
    palette: '999000',
  }, paws5 = {
    sprite: [
      "32223" ],
    palette: '999000',
  }, paws6 = {
    sprite: [
      "3223",
      "3331" ],
    palette: '999000',
  }, paws7 = {
    sprite: [
      "3223",
      "1333" ],
    palette: '999000',
  }, paws8 = {
    sprite: [
      "3223",
      "1331" ],
    palette: '999000',
  }, face = {
    sprite: [
      "1133111111113311",
      "1322311111132231",
      "1322231111322231",
      "1322223333222231",
      "1322222222222231",
      "3222222222222223",
      "3222332222233223",
      "3222332223233223",
      "3211222222222113",
      "3211232232232113",
      "1322233333332231",
      "1132222222222311",
      "1113333333333111" ],
    palette: '999000',
  }
];

var aaoSpriteMaps = [
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 1, 8, tail0], [ 6,17, paws0], [ 6,19, paws4],  [12,19, paws6], [12,19, paws6], [21,19, paws7], [26,19, paws8], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 1, 9, tail1], [ 6,18, paws1], [ 6,18, paws1],  [ 6,19, paws6], [11,19, paws7], [21,19, paws7], [26,19, paws7], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 1,11, tail2], [ 7,18, paws1], [ 7,18, paws1],  [ 7,19, paws6], [12,19, paws7], [22,19, paws7], [27,19, paws7], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 1,11, tail3], [ 6,18, paws1], [ 6,18, paws1],  [ 6,19, paws6], [11,19, paws7], [21,19, paws7], [26,19, paws7], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 0,10, tail4], [ 6,16, paws3], [ 5,18, paws5],  [ 5,19, paws6], [11,19, paws7], [20,19, paws7], [25,19, paws7], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
  [ [ 8, 1,poptart1], [ 9, 3,poptart2], [ 1, 9, tail1], [ 6,16, paws0], [ 5,18, paws5],  [ 5,19, paws6], [11,19, paws6], [20,19, paws6], [25,19, paws7], [18, 6, face], [19, 9, star_eye0], [26, 9, star_eye0], [20,14, cheek], [31,14, cheek] ],
];

var asSprites = [];
var iData = 0, iBits = 0;
for (var i = 0; i < aoSprites.length; i++) {
  var bLastSprite = i == aoSprites.length - 1;
  var oSprite = aoSprites[i];
  var h = oSprite.sprite.length;
  for(var y = 0; y < h; y++) {
    var sLine = oSprite.sprite[y].replace(/^(.+?)1*$/, '$10');
    var bLastLine = y == h - 1;
    if (bLastLine) sLine += '0';
    if (sLine.indexOf('00') != -1 &&
        sLine.indexOf('00') < sLine.length - 2) throw 'assert: ' + i + ':' + y;
    for(var x = 0; x < sLine.length; x++) {
      var bLastPixel = x == sLine.length - 1;
      var iPixel = parseInt(sLine[x]);
      iData += iPixel << iBits;
      iBits += 2;
      if (iBits == 6 || (bLastSprite && bLastLine && bLastPixel)) {
        storeMultiArray(asSprites, iData);
        iData = 0;
        iBits = 0;
      }
    }
  }
}
if (iBits > 0) throw 'assert';
iOffsetSprites = chooseMostEffectiveOffset(asSprites, htmlJavaScriptEncode);
sSprites = asSprites[iOffsetSprites];

var aasSpriteMaps = [];
for (var iFrame = 0; iFrame < aaoSpriteMaps.length; iFrame++) {
  var aoSpritesMap = aaoSpriteMaps[iFrame];
  var asSpriteMaps = [];
  var iData = 0;
  var iBits = 0;
  for (var iSprite = 0; iSprite < aoSpritesMap.length; iSprite++) {
    var x = aoSpritesMap[iSprite][0], 
        y = aoSpritesMap[iSprite][1], 
        oSprite = aoSpritesMap[iSprite][2];
    for (var i = 0; i < aoSprites.length; i++) {
      if (aoSprites[i] === oSprite) {
        break;
      }
    }
    if (i == aoSprites.length) throw 'assert';
    bLastMap = iSprite == aoSpritesMap.length - 1;
    iData += i << iBits;
    iBits += 5;
    iData += x << iBits;
    iBits += 5;
    iData += y << iBits;
    iBits += 5;
    while (iBits > 6 || (iBits > 0 && bLastMap)) {
        storeMultiArray(asSpriteMaps, iData & ((1 << 6) - 1));
        iData >>>= 6;
        iBits -= 6;
    }
  }
  for (var i = 0; i < asSpriteMaps.length; i++) {
    if (iFrame == 0) aasSpriteMaps[i] = [];
    aasSpriteMaps[i][iFrame] = asSpriteMaps[i];
  }
}
if (iBits > 0) throw 'assert: ' + iBits;
iOffsetSpriteMaps = chooseMostEffectiveOffset(aasSpriteMaps, generateStringArrayCode);
asSpriteMaps = aasSpriteMaps[iOffsetSpriteMaps];

// a* = oContext2d;
// A* = fSprite:iDestinationY
// b* = fSprite:iSpriteBits
// c* = oCanvas
// C* = iCode
// d* = fSprite:iSpriteData
// e* = fSprite:iSprite
// f* = fSprite
// g* = fSprite:iPaletteIndex
// h* = iMapBits
// i* = iIndex @ sSprites
// j = 
// k = 
// l* = fSprite:iDestinationX
// m = 
// n* = various counters
// o* = asSpriteMaps
// p = 
// q = 
// r* = sSpriteMap
// R* = iMapData
// s* = sSprites
// S* =
// t* = iCounter;
// u* = u>1 == draw one line lower
// v  = 
// w = 
// x* = fSprite:iSpriteX
// y* = fSprite:iSpriteY
// z = 
t=0;
f=fSprite;
function fSprite(e,c,l,A,S) {
  // Function has two uses: draw a sprite (S is undefined) or draw a rectangle (S is defined)
  // If we need to draw a rectangle do so:
  if (!S||
    a.fillRect(
        6*c, 6*l+6*(u>1), 6*A, 6*S, 
        a.fillStyle = '#' + 'ffff99f39fc9999000036f9ff00f90ff03f009f63f'.substr(3*e, 3))
  )
    // Else parse the sprites and draw when we get to the desired one (e==0):
    for (x=y=i=b = 0;e+1; ) 
      // If the number of double bits(b) in our bit buffer(d) drops to 0, read 3 more (==6 bits)
      b--?0:d=s.charCodeAt(i++,b=2) - iOffsetSprites,
      // Read a double bit from our bit buffer:
      C = d & 3, d >>= 2,
      // Check if the double bit == 0 (end of line or end of sprite)
      C--?
        // No: move to the next pixel and check if this is the desired sprite and if the double bit != 1 (transparent)
        ++x && !e && C-- &&
          // Yes: this pixel should be drawn using the pallete
          f(c<0?4+C:c+C, x+l, y+A, 1, 1):
        // Yes: check if the line length was 0:
        ++y && !x ?
          // Yes: end of sprite, decrease sprite counter(e) and reset y (x is already 0):
          y = --e:
          // No: end of line, set x to 0:
          x=0
}

function fMain() {
  // Draw background blue
  f( 6, u=v=0, R=0, c.width=c.height=400, t++); // 5 arguments = draw rect
  // Draw rainbow & stars
  for (n = 32; n--;)
    n < 8 ?
      // Draw stars (first one is off screen)
        f(                                      // 4 arguments = draw sprite
          2+(n+s.charCodeAt(6*n)+t)%6,          //  sprite#
          0,                                    //  color#
          6*(10 - (s.charCodeAt(6*n)+t)%12),    //  x
          10*n - 2                              //  y
        ):
      // Draw rainbow
      f(                                        // 5 arguments = draw rect
          6+(n>>2),                             //  color#
          8*(n%4)-6+((t>>1))%2,                 //  x
          3*(n>>2)+20+(n+(t>>1))%2,             //  y
          8,                                    //  w
          3                                     //  h
      );
  // draw body
  u = t % 6;
  f(h=5,25,27,21,16);                           // 5 arguments = draw rect
  f(  3,26,26,19,16);                           // 5 arguments = draw rect
  f(  7,27,27,17,14);                           // 5 arguments = draw rect
  for (n++; o[t % 6].charCodeAt(n); h -= 15)
    for (
      u *= u * n != 115; // 5 * 23
      h < 20 || f(                              // 4 arguments = draw sprite
          e = (R >>= 5, R & 31),                //  sprite #
          2 - e,                                //  color #
          17 - (n>8 && (t+2)%6 < 3)
             + (R >>= 5, R & 31),               //  x
          24 + (R >>= 5, R & 31)                //  y
        );
      h+=6
    )
      R+=(o[t % 6].charCodeAt(n++) - iOffsetSpriteMaps) << h
}
var sCodeS = 's="' + htmlJavaScriptEncode(sSprites) + '"';
var sCodeO = 'o=' + generateStringArrayCode(asSpriteMaps);
var iCodeDataLength = [sCodeS, sCodeO].join(';').length;
var sCodeMain = generateCode();
var sOutput = 
    '// data: ~' + iCodeDataLength + ' bytes\r\n' +
    '\r\n' +
    '// sprites: ~' + (sCodeS.length) + ' bytes\r\n' +
    sCodeS + ';\r\n' +
    '// iOffsetSprites = ' + iOffsetSprites + ';\r\n' +
    '\r\n' +
    '// sprite maps: ~' + (sCodeO.length) + ' bytes\r\n' +
    sCodeO + ';\r\n' +
    '// iOffsetSpriteMaps = ' + iOffsetSpriteMaps + ';\r\n' +
    '\r\n' +
    '// code: ~' + sCodeMain.length + ' bytes\r\n' +
    sCodeMain + '\r\n';
document.write('<pre>' + sOutput.replace(/[<&]/g, function (s) { return {'<': '&lt;', '&': '&amp;'}[s];}) + '</pre>');
eval('a=c.getContext("2d");setInterval("' + [htmlJavaScriptEncode(sCodeS), htmlJavaScriptEncode(sCodeO), sCodeMain].join(';') + '",1000,t=65)');

