Source: compression/jxg.js

JXG = {
  exists: (function(undefined) {
    return function(v) {
      return !(v === undefined || v === null);
    };
  })()
};
JXG.decompress = function(str) {
  return unescape((new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(str))).unzip()[0][0]);
};
/*
    Copyright 2008-2012
        Matthias Ehmann,
        Michael Gerhaeuser,
        Carsten Miller,
        Bianca Valentin,
        Alfred Wassermann,
        Peter Wilfahrt

    This file is part of JSXGraph.
    
    Dual licensed under the Apache License Version 2.0, or LGPL Version 3 licenses.

    You should have received a copy of the GNU Lesser General Public License
    along with JSXCompressor.  If not, see <http://www.gnu.org/licenses/>.
    
    You should have received a copy of the Apache License along with JSXCompressor.  
    If not, see <http://www.apache.org/licenses/>.

*/

/**
 * @class Util class
 * @classdesc Utilities for uncompressing and base64 decoding
 * Class for gunzipping, unzipping and base64 decoding of files.
 * It is used for reading GEONExT, Geogebra and Intergeo files.
 *
 * Only Huffman codes are decoded in gunzip.
 * The code is based on the source code for gunzip.c by Pasi Ojala 
 * {@link http://www.cs.tut.fi/~albert/Dev/gunzip/gunzip.c}
 * {@link http://www.cs.tut.fi/~albert}
 */
JXG.Util = {};

/**
 * Unzip zip files
 */
JXG.Util.Unzip = function(barray) {
  var outputArr = [],
    output = "",
    debug = false,
    gpflags,
    files = 0,
    unzipped = [],
    crc,
    buf32k = new Array(32768),
    bIdx = 0,
    modeZIP = false,

    CRC, SIZE,

    bitReverse = [
        0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
        0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
        0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
        0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
        0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
        0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
        0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
        0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
        0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
        0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
        0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
        0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
        0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
        0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
        0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
        0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
        0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
        0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
        0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
        0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
        0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
        0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
        0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
        0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
        0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
        0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
        0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
        0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
        0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
        0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
        0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
        0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
    ],

    cplens = [
        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
    ],

    cplext = [
        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
    ],
    /* 99==invalid */

    cpdist = [
        0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d,
        0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1,
        0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01,
        0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001
    ],

    cpdext = [
        0, 0, 0, 0, 1, 1, 2, 2,
        3, 3, 4, 4, 5, 5, 6, 6,
        7, 7, 8, 8, 9, 9, 10, 10,
        11, 11, 12, 12, 13, 13
    ],

    border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],

    bA = barray,

    bytepos = 0,
    bitpos = 0,
    bb = 1,
    bits = 0,

    NAMEMAX = 256,

    nameBuf = [],

    fileout;

  function readByte() {
    bits += 8;
    if (bytepos < bA.length) {
      //if (debug)
      //    document.write(bytepos+": "+bA[bytepos]+"<br>");
      return bA[bytepos++];
    } else
      return -1;
  }

  function byteAlign() {
    bb = 1;
  }

  function readBit() {
    var carry;
    bits++;
    carry = (bb & 1);
    bb >>= 1;
    if (bb === 0) {
      bb = readByte();
      carry = (bb & 1);
      bb = (bb >> 1) | 0x80;
    }
    return carry;
  }

  function readBits(a) {
    var res = 0,
      i = a;

    while (i--) {
      res = (res << 1) | readBit();
    }
    if (a) {
      res = bitReverse[res] >> (8 - a);
    }
    return res;
  }

  function flushBuffer() {
    //document.write('FLUSHBUFFER:'+buf32k);
    bIdx = 0;
  }

  function addBuffer(a) {
    SIZE++;
    //CRC=updcrc(a,crc);
    buf32k[bIdx++] = a;
    outputArr.push(String.fromCharCode(a));
    //output+=String.fromCharCode(a);
    if (bIdx == 0x8000) {
      //document.write('ADDBUFFER:'+buf32k);
      bIdx = 0;
    }
  }

  function HufNode() {
    this.b0 = 0;
    this.b1 = 0;
    this.jump = null;
    this.jumppos = -1;
  }

  var LITERALS = 288;

  var literalTree = new Array(LITERALS);
  var distanceTree = new Array(32);
  var treepos = 0;
  var Places = null;
  var Places2 = null;

  var impDistanceTree = new Array(64);
  var impLengthTree = new Array(64);

  var len = 0;
  var fpos = new Array(17);
  fpos[0] = 0;
  var flens;
  var fmax;

  function IsPat() {
    while (1) {
      if (fpos[len] >= fmax)
        return -1;
      if (flens[fpos[len]] == len)
        return fpos[len]++;
      fpos[len]++;
    }
  }

  function Rec() {
    var curplace = Places[treepos];
    var tmp;
    if (debug)
      document.write("<br>len:" + len + " treepos:" + treepos);
    if (len == 17) { //war 17
      return -1;
    }
    treepos++;
    len++;

    tmp = IsPat();
    if (debug)
      document.write("<br>IsPat " + tmp);
    if (tmp >= 0) {
      curplace.b0 = tmp; /* leaf cell for 0-bit */
      if (debug)
        document.write("<br>b0 " + curplace.b0);
    } else {
      /* Not a Leaf cell */
      curplace.b0 = 0x8000;
      if (debug)
        document.write("<br>b0 " + curplace.b0);
      if (Rec())
        return -1;
    }
    tmp = IsPat();
    if (tmp >= 0) {
      curplace.b1 = tmp; /* leaf cell for 1-bit */
      if (debug)
        document.write("<br>b1 " + curplace.b1);
      curplace.jump = null; /* Just for the display routine */
    } else {
      /* Not a Leaf cell */
      curplace.b1 = 0x8000;
      if (debug)
        document.write("<br>b1 " + curplace.b1);
      curplace.jump = Places[treepos];
      curplace.jumppos = treepos;
      if (Rec())
        return -1;
    }
    len--;
    return 0;
  }

  function CreateTree(currentTree, numval, lengths, show) {
    var i;
    /* Create the Huffman decode tree/table */
    //document.write("<br>createtree<br>");
    if (debug)
      document.write("currentTree " + currentTree + " numval " + numval + " lengths " + lengths + " show " + show);
    Places = currentTree;
    treepos = 0;
    flens = lengths;
    fmax = numval;
    for (i = 0; i < 17; i++)
      fpos[i] = 0;
    len = 0;
    if (Rec()) {
      //fprintf(stderr, "invalid huffman tree\n");
      if (debug)
        alert("invalid huffman tree\n");
      return -1;
    }
    if (debug) {
      document.write('<br>Tree: ' + Places.length);
      for (var a = 0; a < 32; a++) {
        document.write("Places[" + a + "].b0=" + Places[a].b0 + "<br>");
        document.write("Places[" + a + "].b1=" + Places[a].b1 + "<br>");
      }
    }

    /*if(show) {
            var tmp;
            for(tmp=currentTree;tmp<Places;tmp++) {
                fprintf(stdout, "0x%03x  0x%03x (0x%04x)",tmp-currentTree, tmp->jump?tmp->jump-currentTree:0,(tmp->jump?tmp->jump-currentTree:0)*6+0xcf0);
                if(!(tmp.b0 & 0x8000)) {
                    //fprintf(stdout, "  0x%03x (%c)", tmp->b0,(tmp->b0<256 && isprint(tmp->b0))?tmp->b0:'�');
                }
                if(!(tmp.b1 & 0x8000)) {
                    if((tmp.b0 & 0x8000))
                        fprintf(stdout, "           ");
                    fprintf(stdout, "  0x%03x (%c)", tmp->b1,(tmp->b1<256 && isprint(tmp->b1))?tmp->b1:'�');
                }
                fprintf(stdout, "\n");
            }
        }*/
    return 0;
  }

  function DecodeValue(currentTree) {
    var len, i,
      xtreepos = 0,
      X = currentTree[xtreepos],
      b;

    /* decode one symbol of the data */
    while (1) {
      b = readBit();
      if (debug)
        document.write("b=" + b);
      if (b) {
        if (!(X.b1 & 0x8000)) {
          if (debug)
            document.write("ret1");
          return X.b1; /* If leaf node, return data */
        }
        X = X.jump;
        len = currentTree.length;
        for (i = 0; i < len; i++) {
          if (currentTree[i] === X) {
            xtreepos = i;
            break;
          }
        }
        //xtreepos++;
      } else {
        if (!(X.b0 & 0x8000)) {
          if (debug)
            document.write("ret2");
          return X.b0; /* If leaf node, return data */
        }
        //X++; //??????????????????
        xtreepos++;
        X = currentTree[xtreepos];
      }
    }
  }

  function DeflateLoop() {
    var last, c, type, i, j, len, dist;

    do {
      /*if((last = readBit())){
            fprintf(errfp, "Last Block: ");
        } else {
            fprintf(errfp, "Not Last Block: ");
        }*/
      last = readBit();
      type = readBits(2);
      switch (type) {
        case 0:
          if (debug)
            alert("Stored\n");
          break;
        case 1:
          if (debug)
            alert("Fixed Huffman codes\n");
          break;
        case 2:
          if (debug)
            alert("Dynamic Huffman codes\n");
          break;
        case 3:
          if (debug)
            alert("Reserved block type!!\n");
          break;
        default:
          if (debug)
            alert("Unexpected value %d!\n", type);
          break;
      }

      if (type === 0) {
        var blockLen, cSum;

        // Stored 
        byteAlign();
        blockLen = readByte();
        blockLen |= (readByte() << 8);

        cSum = readByte();
        cSum |= (readByte() << 8);

        if (((blockLen ^ ~cSum) & 0xffff)) {
          document.write("BlockLen checksum mismatch\n");
        }
        while (blockLen--) {
          c = readByte();
          addBuffer(c);
        }
      } else if (type == 1) {
        /* Fixed Huffman tables -- fixed decode routine */
        while (1) {
          /*
                256    0000000        0
                :   :     :
                279    0010111        23
                0   00110000    48
                :    :      :
                143    10111111    191
                280 11000000    192
                :    :      :
                287 11000111    199
                144    110010000    400
                :    :       :
                255    111111111    511
    
                Note the bit order!
                */

          j = (bitReverse[readBits(7)] >> 1);
          if (j > 23) {
            j = (j << 1) | readBit(); /* 48..255 */

            if (j > 199) { /* 200..255 */
              j -= 128; /*  72..127 */
              j = (j << 1) | readBit(); /* 144..255 << */
            } else { /*  48..199 */
              j -= 48; /*   0..151 */
              if (j > 143) {
                j = j + 136; /* 280..287 << */
                /*   0..143 << */
              }
            }
          } else { /*   0..23 */
            j += 256; /* 256..279 << */
          }
          if (j < 256) {
            addBuffer(j);
            //document.write("out:"+String.fromCharCode(j));
            /*fprintf(errfp, "@%d %02x\n", SIZE, j);*/
          } else if (j == 256) {
            /* EOF */
            break;
          } else {
            j -= 256 + 1; /* bytes + EOF */
            len = readBits(cplext[j]) + cplens[j];

            j = bitReverse[readBits(5)] >> 3;
            if (cpdext[j] > 8) {
              dist = readBits(8);
              dist |= (readBits(cpdext[j] - 8) << 8);
            } else {
              dist = readBits(cpdext[j]);
            }
            dist += cpdist[j];

            /*fprintf(errfp, "@%d (l%02x,d%04x)\n", SIZE, len, dist);*/
            for (j = 0; j < len; j++) {
              c = buf32k[(bIdx - dist) & 0x7fff];
              addBuffer(c);
            }
          }
        } // while
      } else if (type == 2) {
        var n, literalCodes, distCodes, lenCodes;
        var ll = new Array(288 + 32); // "static" just to preserve stack

        // Dynamic Huffman tables 

        literalCodes = 257 + readBits(5);
        distCodes = 1 + readBits(5);
        lenCodes = 4 + readBits(4);
        //document.write("<br>param: "+literalCodes+" "+distCodes+" "+lenCodes+"<br>");
        for (j = 0; j < 19; j++) {
          ll[j] = 0;
        }

        // Get the decode tree code lengths

        //document.write("<br>");
        for (j = 0; j < lenCodes; j++) {
          ll[border[j]] = readBits(3);
          //document.write(ll[border[j]]+" ");
        }
        //fprintf(errfp, "\n");
        //document.write('<br>ll:'+ll);
        len = distanceTree.length;
        for (i = 0; i < len; i++)
          distanceTree[i] = new HufNode();
        if (CreateTree(distanceTree, 19, ll, 0)) {
          flushBuffer();
          return 1;
        }
        if (debug) {
          document.write("<br>distanceTree");
          for (var a = 0; a < distanceTree.length; a++) {
            document.write("<br>" + distanceTree[a].b0 + " " + distanceTree[a].b1 + " " + distanceTree[a].jump + " " +
              distanceTree[a].jumppos);
            /*if (distanceTree[a].jumppos!=-1)
                document.write(" "+distanceTree[a].jump.b0+" "+distanceTree[a].jump.b1);
             */
          }
        }
        //document.write('<BR>tree created');

        //read in literal and distance code lengths
        n = literalCodes + distCodes;
        i = 0;
        var z = -1;
        if (debug)
          document.write("<br>n=" + n + " bits: " + bits + "<br>");
        while (i < n) {
          z++;
          j = DecodeValue(distanceTree);
          if (debug)
            document.write("<br>" + z + " i:" + i + " decode: " + j + "    bits " + bits + "<br>");
          if (j < 16) { // length of code in bits (0..15)
            ll[i++] = j;
          } else if (j == 16) { // repeat last length 3 to 6 times 
            var l;
            j = 3 + readBits(2);
            if (i + j > n) {
              flushBuffer();
              return 1;
            }
            l = i ? ll[i - 1] : 0;
            while (j--) {
              ll[i++] = l;
            }
          } else {
            if (j == 17) { // 3 to 10 zero length codes
              j = 3 + readBits(3);
            } else { // j == 18: 11 to 138 zero length codes 
              j = 11 + readBits(7);
            }
            if (i + j > n) {
              flushBuffer();
              return 1;
            }
            while (j--) {
              ll[i++] = 0;
            }
          }
        }
        /*for(j=0; j<literalCodes+distCodes; j++) {
                //fprintf(errfp, "%d ", ll[j]);
                if ((j&7)==7)
                    fprintf(errfp, "\n");
            }
            fprintf(errfp, "\n");*/
        // Can overwrite tree decode tree as it is not used anymore
        len = literalTree.length;
        for (i = 0; i < len; i++)
          literalTree[i] = new HufNode();
        if (CreateTree(literalTree, literalCodes, ll, 0)) {
          flushBuffer();
          return 1;
        }
        len = literalTree.length;
        for (i = 0; i < len; i++)
          distanceTree[i] = new HufNode();
        var ll2 = [];
        for (i = literalCodes; i < ll.length; i++) {
          ll2[i - literalCodes] = ll[i];
        }
        if (CreateTree(distanceTree, distCodes, ll2, 0)) {
          flushBuffer();
          return 1;
        }
        if (debug)
          document.write("<br>literalTree");
        outer: while (1) {
          j = DecodeValue(literalTree);
          if (j >= 256) { // In C64: if carry set
            j -= 256;
            if (j === 0) {
              // EOF
              break;
            }
            j--;
            len = readBits(cplext[j]) + cplens[j];

            j = DecodeValue(distanceTree);
            if (cpdext[j] > 8) {
              dist = readBits(8);
              dist |= (readBits(cpdext[j] - 8) << 8);
            } else {
              dist = readBits(cpdext[j]);
            }
            dist += cpdist[j];
            while (len--) {
              if (bIdx - dist < 0) {
                break outer;
              }
              c = buf32k[(bIdx - dist) & 0x7fff];
              addBuffer(c);
            }
          } else {
            addBuffer(j);
          }
        }
      }
    } while (!last);
    flushBuffer();

    byteAlign();
    return 0;
  }

  JXG.Util.Unzip.prototype.unzipFile = function(name) {
    var i;
    this.unzip();
    //alert(unzipped[0][1]);
    for (i = 0; i < unzipped.length; i++) {
      if (unzipped[i][1] == name) {
        return unzipped[i][0];
      }
    }
  };

  JXG.Util.Unzip.prototype.deflate = function() {
    outputArr = [];
    var tmp = [];
    modeZIP = false;
    DeflateLoop();
    if (debug)
      alert(outputArr.join(''));
    unzipped[files] = new Array(2);
    unzipped[files][0] = outputArr.join('');
    unzipped[files][1] = "DEFLATE";
    files++;
    return unzipped;
  };

  JXG.Util.Unzip.prototype.unzip = function() {
    //convertToByteArray(input);
    if (debug)
      alert(bA);
    /*for (i=0;i<bA.length*8;i++){
		document.write(readBit());
		if ((i+1)%8==0)
			document.write(" ");
	}*/
    /*for (i=0;i<bA.length;i++){
		document.write(readByte()+" ");
		if ((i+1)%8==0)
			document.write(" ");
	}
	for (i=0;i<bA.length;i++){
		document.write(bA[i]+" ");
		if ((i+1)%16==0)
			document.write("<br>");
	}	
	*/
    //alert(bA);
    nextFile();
    return unzipped;
  };

  function nextFile() {
    if (debug)
      alert("NEXTFILE");
    outputArr = [];
    var tmp = [];
    modeZIP = false;
    tmp[0] = readByte();
    tmp[1] = readByte();
    if (debug)
      alert("type: " + tmp[0] + " " + tmp[1]);
    if (tmp[0] == parseInt("78", 16) && tmp[1] == parseInt("da", 16)) { //GZIP
      if (debug)
        alert("GEONExT-GZIP");
      DeflateLoop();
      if (debug)
        alert(outputArr.join(''));
      unzipped[files] = new Array(2);
      unzipped[files][0] = outputArr.join('');
      unzipped[files][1] = "geonext.gxt";
      files++;
    }
    if (tmp[0] == parseInt("78", 16) && tmp[1] == parseInt("9c", 16)) { //ZLIB
      if (debug)
        alert("ZLIB");
      DeflateLoop();
      if (debug)
        alert(outputArr.join(''));
      unzipped[files] = new Array(2);
      unzipped[files][0] = outputArr.join('');
      unzipped[files][1] = "ZLIB";
      files++;
    }
    if (tmp[0] == parseInt("1f", 16) && tmp[1] == parseInt("8b", 16)) { //GZIP
      if (debug)
        alert("GZIP");
      //DeflateLoop();
      skipdir();
      if (debug)
        alert(outputArr.join(''));
      unzipped[files] = new Array(2);
      unzipped[files][0] = outputArr.join('');
      unzipped[files][1] = "file";
      files++;
    }
    if (tmp[0] == parseInt("50", 16) && tmp[1] == parseInt("4b", 16)) { //ZIP
      modeZIP = true;
      tmp[2] = readByte();
      tmp[3] = readByte();
      if (tmp[2] == parseInt("3", 16) && tmp[3] == parseInt("4", 16)) {
        //MODE_ZIP
        tmp[0] = readByte();
        tmp[1] = readByte();
        if (debug)
          alert("ZIP-Version: " + tmp[1] + " " + tmp[0] / 10 + "." + tmp[0] % 10);

        gpflags = readByte();
        gpflags |= (readByte() << 8);
        if (debug)
          alert("gpflags: " + gpflags);

        var method = readByte();
        method |= (readByte() << 8);
        if (debug)
          alert("method: " + method);

        readByte();
        readByte();
        readByte();
        readByte();

        var crc = readByte();
        crc |= (readByte() << 8);
        crc |= (readByte() << 16);
        crc |= (readByte() << 24);

        var compSize = readByte();
        compSize |= (readByte() << 8);
        compSize |= (readByte() << 16);
        compSize |= (readByte() << 24);

        var size = readByte();
        size |= (readByte() << 8);
        size |= (readByte() << 16);
        size |= (readByte() << 24);

        if (debug)
          alert("local CRC: " + crc + "\nlocal Size: " + size + "\nlocal CompSize: " + compSize);

        var filelen = readByte();
        filelen |= (readByte() << 8);

        var extralen = readByte();
        extralen |= (readByte() << 8);

        if (debug)
          alert("filelen " + filelen);
        i = 0;
        nameBuf = [];
        var c;
        while (filelen--) {
          c = readByte();
          if (c == "/" | c == ":") {
            i = 0;
          } else if (i < NAMEMAX - 1)
            nameBuf[i++] = String.fromCharCode(c);
        }
        if (debug)
          alert("nameBuf: " + nameBuf);

        //nameBuf[i] = "\0";
        if (!fileout)
          fileout = nameBuf;

        var i = 0;
        while (i < extralen) {
          c = readByte();
          i++;
        }

        CRC = 0xffffffff;
        SIZE = 0;

        if (size === 0 && fileOut.charAt(fileout.length - 1) == "/") {
          //skipdir
          if (debug)
            alert("skipdir");
        }
        if (method == 8) {
          DeflateLoop();
          if (debug)
            alert(outputArr.join(''));
          unzipped[files] = new Array(2);
          unzipped[files][0] = outputArr.join('');
          unzipped[files][1] = nameBuf.join('');
          files++;
          //return outputArr.join('');
        }
        skipdir();
      }
    }
  }

  function skipdir() {
    var crc,
      tmp = [],
      compSize, size, os, i, c;

    if ((gpflags & 8)) {
      tmp[0] = readByte();
      tmp[1] = readByte();
      tmp[2] = readByte();
      tmp[3] = readByte();

      if (tmp[0] == parseInt("50", 16) &&
        tmp[1] == parseInt("4b", 16) &&
        tmp[2] == parseInt("07", 16) &&
        tmp[3] == parseInt("08", 16)) {
        crc = readByte();
        crc |= (readByte() << 8);
        crc |= (readByte() << 16);
        crc |= (readByte() << 24);
      } else {
        crc = tmp[0] | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24);
      }

      compSize = readByte();
      compSize |= (readByte() << 8);
      compSize |= (readByte() << 16);
      compSize |= (readByte() << 24);

      size = readByte();
      size |= (readByte() << 8);
      size |= (readByte() << 16);
      size |= (readByte() << 24);

      if (debug)
        alert("CRC:");
    }

    if (modeZIP)
      nextFile();

    tmp[0] = readByte();
    if (tmp[0] != 8) {
      if (debug)
        alert("Unknown compression method!");
      return 0;
    }

    gpflags = readByte();
    if (debug) {
      if ((gpflags & ~(parseInt("1f", 16))))
        alert("Unknown flags set!");
    }

    readByte();
    readByte();
    readByte();
    readByte();

    readByte();
    os = readByte();

    if ((gpflags & 4)) {
      tmp[0] = readByte();
      tmp[2] = readByte();
      len = tmp[0] + 256 * tmp[1];
      if (debug)
        alert("Extra field size: " + len);
      for (i = 0; i < len; i++)
        readByte();
    }

    if ((gpflags & 8)) {
      i = 0;
      nameBuf = [];
      while (c = readByte()) {
        if (c == "7" || c == ":")
          i = 0;
        if (i < NAMEMAX - 1)
          nameBuf[i++] = c;
      }
      //nameBuf[i] = "\0";
      if (debug)
        alert("original file name: " + nameBuf);
    }

    if ((gpflags & 16)) {
      while (c = readByte()) {
        //FILE COMMENT
      }
    }

    if ((gpflags & 2)) {
      readByte();
      readByte();
    }

    DeflateLoop();

    crc = readByte();
    crc |= (readByte() << 8);
    crc |= (readByte() << 16);
    crc |= (readByte() << 24);

    size = readByte();
    size |= (readByte() << 8);
    size |= (readByte() << 16);
    size |= (readByte() << 24);

    if (modeZIP)
      nextFile();

  }

};

/**
 *  Base64 encoding / decoding
 *  {@link http://www.webtoolkit.info/}
 */
JXG.Util.Base64 = {

  // private property
  _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

  // public method for encoding
  encode: function(input) {
    var output = [],
      chr1, chr2, chr3, enc1, enc2, enc3, enc4,
      i = 0;

    input = JXG.Util.Base64._utf8_encode(input);

    while (i < input.length) {

      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);

      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;

      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
        enc4 = 64;
      }

      output.push([this._keyStr.charAt(enc1),
          this._keyStr.charAt(enc2),
          this._keyStr.charAt(enc3),
          this._keyStr.charAt(enc4)
      ].join(''));
    }

    return output.join('');
  },

  // public method for decoding
  decode: function(input, utf8) {
    var output = [],
      chr1, chr2, chr3,
      enc1, enc2, enc3, enc4,
      i = 0;

    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

    while (i < input.length) {

      enc1 = this._keyStr.indexOf(input.charAt(i++));
      enc2 = this._keyStr.indexOf(input.charAt(i++));
      enc3 = this._keyStr.indexOf(input.charAt(i++));
      enc4 = this._keyStr.indexOf(input.charAt(i++));

      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;

      output.push(String.fromCharCode(chr1));

      if (enc3 != 64) {
        output.push(String.fromCharCode(chr2));
      }
      if (enc4 != 64) {
        output.push(String.fromCharCode(chr3));
      }
    }

    output = output.join('');

    if (utf8) {
      output = JXG.Util.Base64._utf8_decode(output);
    }
    return output;

  },

  // private method for UTF-8 encoding
  _utf8_encode: function(string) {
    string = string.replace(/\r\n/g, "\n");
    var utftext = "";

    for (var n = 0; n < string.length; n++) {

      var c = string.charCodeAt(n);

      if (c < 128) {
        utftext += String.fromCharCode(c);
      } else if ((c > 127) && (c < 2048)) {
        utftext += String.fromCharCode((c >> 6) | 192);
        utftext += String.fromCharCode((c & 63) | 128);
      } else {
        utftext += String.fromCharCode((c >> 12) | 224);
        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
        utftext += String.fromCharCode((c & 63) | 128);
      }

    }

    return utftext;
  },

  // private method for UTF-8 decoding
  _utf8_decode: function(utftext) {
    var string = [],
      i = 0,
      c = 0,
      c2 = 0,
      c3 = 0;

    while (i < utftext.length) {
      c = utftext.charCodeAt(i);
      if (c < 128) {
        string.push(String.fromCharCode(c));
        i++;
      } else if ((c > 191) && (c < 224)) {
        c2 = utftext.charCodeAt(i + 1);
        string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
        i += 2;
      } else {
        c2 = utftext.charCodeAt(i + 1);
        c3 = utftext.charCodeAt(i + 2);
        string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
        i += 3;
      }
    }
    return string.join('');
  },

  _destrip: function(stripped, wrap) {
    var lines = [],
      lineno, i,
      destripped = [];

    if (wrap === null)
      wrap = 76;

    stripped.replace(/ /g, "");
    lineno = stripped.length / wrap;
    for (i = 0; i < lineno; i++)
      lines[i] = stripped.substr(i * wrap, wrap);
    if (lineno != stripped.length / wrap)
      lines[lines.length] = stripped.substr(lineno * wrap, stripped.length - (lineno * wrap));

    for (i = 0; i < lines.length; i++)
      destripped.push(lines[i]);
    return destripped.join('\n');
  },

  decodeAsArray: function(input) {
    var dec = this.decode(input),
      ar = [],
      i;
    for (i = 0; i < dec.length; i++) {
      ar[i] = dec.charCodeAt(i);
    }
    return ar;
  },

  decodeGEONExT: function(input) {
    return decodeAsArray(destrip(input), false);
  }
};

/**
 * @private
 */
JXG.Util.asciiCharCodeAt = function(str, i) {
  var c = str.charCodeAt(i);
  if (c > 255) {
    switch (c) {
      case 8364:
        c = 128;
        break;
      case 8218:
        c = 130;
        break;
      case 402:
        c = 131;
        break;
      case 8222:
        c = 132;
        break;
      case 8230:
        c = 133;
        break;
      case 8224:
        c = 134;
        break;
      case 8225:
        c = 135;
        break;
      case 710:
        c = 136;
        break;
      case 8240:
        c = 137;
        break;
      case 352:
        c = 138;
        break;
      case 8249:
        c = 139;
        break;
      case 338:
        c = 140;
        break;
      case 381:
        c = 142;
        break;
      case 8216:
        c = 145;
        break;
      case 8217:
        c = 146;
        break;
      case 8220:
        c = 147;
        break;
      case 8221:
        c = 148;
        break;
      case 8226:
        c = 149;
        break;
      case 8211:
        c = 150;
        break;
      case 8212:
        c = 151;
        break;
      case 732:
        c = 152;
        break;
      case 8482:
        c = 153;
        break;
      case 353:
        c = 154;
        break;
      case 8250:
        c = 155;
        break;
      case 339:
        c = 156;
        break;
      case 382:
        c = 158;
        break;
      case 376:
        c = 159;
        break;
      default:
        break;
    }
  }
  return c;
};

/**
 * Decoding string into utf-8
 * @param {String} string to decode
 * @return {String} utf8 decoded string
 */
JXG.Util.utf8Decode = function(utftext) {
  var string = [];
  var i = 0;
  var c = 0,
    c1 = 0,
    c2 = 0,
    c3;
  if (!JXG.exists(utftext)) return '';

  while (i < utftext.length) {
    c = utftext.charCodeAt(i);

    if (c < 128) {
      string.push(String.fromCharCode(c));
      i++;
    } else if ((c > 191) && (c < 224)) {
      c2 = utftext.charCodeAt(i + 1);
      string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
      i += 2;
    } else {
      c2 = utftext.charCodeAt(i + 1);
      c3 = utftext.charCodeAt(i + 2);
      string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
      i += 3;
    }
  }
  return string.join('');
};

/**
 * Generate a random uuid.
 * http://www.broofa.com
 * mailto:robert@broofa.com
 *
 * Copyright (c) 2010 Robert Kieffer
 * Dual licensed under the MIT and GPL licenses.
 *
 * EXAMPLES:
 *   >>> Math.uuid()
 *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
 */
JXG.Util.genUUID = function() {
  // Private array of chars to use
  var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''),
    uuid = new Array(36),
    rnd = 0,
    r;

  for (var i = 0; i < 36; i++) {
    if (i == 8 || i == 13 || i == 18 || i == 23) {
      uuid[i] = '-';
    } else if (i == 14) {
      uuid[i] = '4';
    } else {
      if (rnd <= 0x02) rnd = 0x2000000 + (Math.random() * 0x1000000) | 0;
      r = rnd & 0xf;
      rnd = rnd >> 4;
      uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
    }
  }

  return uuid.join('');
};


module.exports = JXG;