      1 /*
      2     Copyright 2008,2009
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26 /**
     27  * @fileoverview Utilities for uncompressing and base64 decoding
     28  */
     29
     30 /**
     31   * @class Util class
     32   * Class for gunzipping, unzipping and base64 decoding of files.
     33   * It is used for reading GEONExT, Geogebra and Intergeo files.
     34   *
     35   * Only Huffman codes are decoded in gunzip.
     36   * The code is based on the source code for gunzip.c by Pasi Ojala
     37   * @see <a href="http://www.cs.tut.fi/~albert/Dev/gunzip/gunzip.c">http://www.cs.tut.fi/~albert/Dev/gunzip/gunzip.c</a>
     38   * @see <a href="http://www.cs.tut.fi/~albert">http://www.cs.tut.fi/~albert</a>
     39   */
     40 JXG.Util = {};
     41
     42 /**
     43  * Unzip zip files
     44  */
     45 JXG.Util.Unzip = function (barray){
     46     var outputArr = [],
     47         output = "",
     48         debug = false,
     49         gpflags,
     50         files = 0,
     51         unzipped = [],
     52         crc,
     53         buf32k = new Array(32768),
     54         bIdx = 0,
     55         modeZIP=false,
     56
     57         CRC, SIZE,
     58
     59         bitReverse = [
     60         0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
     61         0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
     62         0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
     63         0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
     64         0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
     65         0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
     66         0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
     67         0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
     68         0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
     69         0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
     70         0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
     71         0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
     72         0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
     73         0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
     74         0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
     75         0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
     76         0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
     77         0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
     78         0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
     79         0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
     80         0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
     81         0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
     82         0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
     83         0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
     84         0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
     85         0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
     86         0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
     87         0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
     88         0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
     89         0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
     90         0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
     91         0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
     92     ],
     93
     94     cplens = [
     95         3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
     96         35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
     97     ],
     98
     99     cplext = [
    100         0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
    101         3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
    102     ], /* 99==invalid */
    103
    104     cpdist = [
    105         0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d,
    106         0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1,
    107         0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01,
    108         0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001
    109     ],
    110
    111     cpdext = [
    112         0,  0,  0,  0,  1,  1,  2,  2,
    113         3,  3,  4,  4,  5,  5,  6,  6,
    114         7,  7,  8,  8,  9,  9, 10, 10,
    115         11, 11, 12, 12, 13, 13
    116     ],
    117
    118     border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
    119
    120     bA = barray,
    121
    122     bytepos=0,
    123     bitpos=0,
    124     bb = 1,
    125     bits=0,
    126
    127     NAMEMAX = 256,
    128
    129     nameBuf = [],
    130
    131     fileout;
    132
    133     function readByte(){
    134         bits+=8;
    135         if (bytepos<bA.length){
    136             //if (debug)
    137             //    document.write(bytepos+": "+bA[bytepos]+"<br>");
    138             return bA[bytepos++];
    139         } else
    140             return -1;
    141     };
    142
    143     function byteAlign(){
    144         bb = 1;
    145     };
    146
    147     function readBit(){
    148         var carry;
    149         bits++;
    150         carry = (bb & 1);
    151         bb >>= 1;
    152         if (bb==0){
    153             bb = readByte();
    154             carry = (bb & 1);
    155             bb = (bb>>1) | 0x80;
    156         }
    157         return carry;
    158     };
    159
    160     function readBits(a) {
    161         var res = 0,
    162             i = a;
    163
    164         while(i--) {
    165             res = (res<<1) | readBit();
    166         }
    167         if(a) {
    168             res = bitReverse[res]>>(8-a);
    169         }
    170         return res;
    171     };
    172
    173     function flushBuffer(){
    174         //document.write('FLUSHBUFFER:'+buf32k);
    175         bIdx = 0;
    176     };
    177     function addBuffer(a){
    178         SIZE++;
    179         //CRC=updcrc(a,crc);
    180         buf32k[bIdx++] = a;
    181         outputArr.push(String.fromCharCode(a));
    182         //output+=String.fromCharCode(a);
    183         if(bIdx==0x8000){
    184             //document.write('ADDBUFFER:'+buf32k);
    185             bIdx=0;
    186         }
    187     };
    188
    189     function HufNode() {
    190         this.b0=0;
    191         this.b1=0;
    192         this.jump = null;
    193         this.jumppos = -1;
    194     };
    195
    196     var LITERALS = 288;
    197
    198     var literalTree = new Array(LITERALS);
    199     var distanceTree = new Array(32);
    200     var treepos=0;
    201     var Places = null;
    202     var Places2 = null;
    203
    204     var impDistanceTree = new Array(64);
    205     var impLengthTree = new Array(64);
    206
    207     var len = 0;
    208     var fpos = new Array(17);
    209     fpos[0]=0;
    210     var flens;
    211     var fmax;
    212
    213     function IsPat() {
    214         while (1) {
    215             if (fpos[len] >= fmax)
    216                 return -1;
    217             if (flens[fpos[len]] == len)
    218                 return fpos[len]++;
    219             fpos[len]++;
    220         }
    221     };
    222
    223     function Rec() {
    224         var curplace = Places[treepos];
    225         var tmp;
    226         if (debug)
    227     		document.write("<br>len:"+len+" treepos:"+treepos);
    228         if(len==17) { //war 17
    229             return -1;
    230         }
    231         treepos++;
    232         len++;
    233
    234         tmp = IsPat();
    235         if (debug)
    236         	document.write("<br>IsPat "+tmp);
    237         if(tmp >= 0) {
    238             curplace.b0 = tmp;    /* leaf cell for 0-bit */
    239             if (debug)
    240             	document.write("<br>b0 "+curplace.b0);
    241         } else {
    242         /* Not a Leaf cell */
    243         curplace.b0 = 0x8000;
    244         if (debug)
    245         	document.write("<br>b0 "+curplace.b0);
    246         if(Rec())
    247             return -1;
    248         }
    249         tmp = IsPat();
    250         if(tmp >= 0) {
    251             curplace.b1 = tmp;    /* leaf cell for 1-bit */
    252             if (debug)
    253             	document.write("<br>b1 "+curplace.b1);
    254             curplace.jump = null;    /* Just for the display routine */
    255         } else {
    256             /* Not a Leaf cell */
    257             curplace.b1 = 0x8000;
    258             if (debug)
    259             	document.write("<br>b1 "+curplace.b1);
    260             curplace.jump = Places[treepos];
    261             curplace.jumppos = treepos;
    262             if(Rec())
    263                 return -1;
    264         }
    265         len--;
    266         return 0;
    267     };
    268
    269     function CreateTree(currentTree, numval, lengths, show) {
    270         var i;
    271         /* Create the Huffman decode tree/table */
    272         //document.write("<br>createtree<br>");
    273         if (debug)
    274         	document.write("currentTree "+currentTree+" numval "+numval+" lengths "+lengths+" show "+show);
    275         Places = currentTree;
    276         treepos=0;
    277         flens = lengths;
    278         fmax  = numval;
    279         for (i=0;i<17;i++)
    280             fpos[i] = 0;
    281         len = 0;
    282         if(Rec()) {
    283             //fprintf(stderr, "invalid huffman tree\n");
    284             if (debug)
    285             	alert("invalid huffman tree\n");
    286             return -1;
    287         }
    288         if (debug){
    289         	document.write('<br>Tree: '+Places.length);
    290         	for (var a=0;a<32;a++){
    291             	document.write("Places["+a+"].b0="+Places[a].b0+"<br>");
    292             	document.write("Places["+a+"].b1="+Places[a].b1+"<br>");
    293         	}
    294         }
    295
    296         /*if(show) {
    297             var tmp;
    298             for(tmp=currentTree;tmp<Places;tmp++) {
    299                 fprintf(stdout, "0x%03x  0x%03x (0x%04x)",tmp-currentTree, tmp->jump?tmp->jump-currentTree:0,(tmp->jump?tmp->jump-currentTree:0)*6+0xcf0);
    300                 if(!(tmp.b0 & 0x8000)) {
    301                     //fprintf(stdout, "  0x%03x (%c)", tmp->b0,(tmp->b0<256 && isprint(tmp->b0))?tmp->b0:'�');
    302                 }
    303                 if(!(tmp.b1 & 0x8000)) {
    304                     if((tmp.b0 & 0x8000))
    305                         fprintf(stdout, "           ");
    306                     fprintf(stdout, "  0x%03x (%c)", tmp->b1,(tmp->b1<256 && isprint(tmp->b1))?tmp->b1:'�');
    307                 }
    308                 fprintf(stdout, "\n");
    309             }
    310         }*/
    311         return 0;
    312     };
    313
    314     function DecodeValue(currentTree) {
    315         var len, i,
    316             xtreepos=0,
    317             X = currentTree[xtreepos],
    318             b;
    319
    320         /* decode one symbol of the data */
    321         while(1) {
    322             b=readBit();
    323             if (debug)
    324             	document.write("b="+b);
    325             if(b) {
    326                 if(!(X.b1 & 0x8000)){
    327                 	if (debug)
    328                     	document.write("ret1");
    329                     return X.b1;    /* If leaf node, return data */
    330                 }
    331                 X = X.jump;
    332                 len = currentTree.length;
    333                 for (i=0;i<len;i++){
    334                     if (currentTree[i]===X){
    335                         xtreepos=i;
    336                         break;
    337                     }
    338                 }
    339                 //xtreepos++;
    340             } else {
    341                 if(!(X.b0 & 0x8000)){
    342                 	if (debug)
    343                     	document.write("ret2");
    344                     return X.b0;    /* If leaf node, return data */
    345                 }
    346                 //X++; //??????????????????
    347                 xtreepos++;
    348                 X = currentTree[xtreepos];
    349             }
    350         }
    351         if (debug)
    352         	document.write("ret3");
    353         return -1;
    354     };
    355
    356     function DeflateLoop() {
    357     var last, c, type, i, len;
    358
    359     do {
    360         /*if((last = readBit())){
    361             fprintf(errfp, "Last Block: ");
    362         } else {
    363             fprintf(errfp, "Not Last Block: ");
    364         }*/
    365         last = readBit();
    366         type = readBits(2);
    367         switch(type) {
    368             case 0:
    369             	if (debug)
    370                 	alert("Stored\n");
    371                 break;
    372             case 1:
    373             	if (debug)
    374                 	alert("Fixed Huffman codes\n");
    375                 break;
    376             case 2:
    377             	if (debug)
    378                 	alert("Dynamic Huffman codes\n");
    379                 break;
    380             case 3:
    381             	if (debug)
    382                 	alert("Reserved block type!!\n");
    383                 break;
    384             default:
    385             	if (debug)
    386                 	alert("Unexpected value %d!\n", type);
    387                 break;
    388         }
    389
    390         if(type==0) {
    391             var blockLen, cSum;
    392
    393             // Stored
    394             byteAlign();
    395             blockLen = readByte();
    396             blockLen |= (readByte()<<8);
    397
    398             cSum = readByte();
    399             cSum |= (readByte()<<8);
    400
    401             if(((blockLen ^ ~cSum) & 0xffff)) {
    402                 document.write("BlockLen checksum mismatch\n");
    403             }
    404             while(blockLen--) {
    405                 c = readByte();
    406                 addBuffer(c);
    407             }
    408         } else if(type==1) {
    409             var j;
    410
    411             /* Fixed Huffman tables -- fixed decode routine */
    412             while(1) {
    413             /*
    414                 256    0000000        0
    415                 :   :     :
    416                 279    0010111        23
    417                 0   00110000    48
    418                 :    :      :
    419                 143    10111111    191
    420                 280 11000000    192
    421                 :    :      :
    422                 287 11000111    199
    423                 144    110010000    400
    424                 :    :       :
    425                 255    111111111    511
    426
    427                 Note the bit order!
    428                 */
    429
    430             j = (bitReverse[readBits(7)]>>1);
    431             if(j > 23) {
    432                 j = (j<<1) | readBit();    /* 48..255 */
    433
    434                 if(j > 199) {    /* 200..255 */
    435                     j -= 128;    /*  72..127 */
    436                     j = (j<<1) | readBit();        /* 144..255 << */
    437                 } else {        /*  48..199 */
    438                     j -= 48;    /*   0..151 */
    439                     if(j > 143) {
    440                         j = j+136;    /* 280..287 << */
    441                         /*   0..143 << */
    442                     }
    443                 }
    444             } else {    /*   0..23 */
    445                 j += 256;    /* 256..279 << */
    446             }
    447             if(j < 256) {
    448                 addBuffer(j);
    449                 //document.write("out:"+String.fromCharCode(j));
    450                 /*fprintf(errfp, "@%d %02x\n", SIZE, j);*/
    451             } else if(j == 256) {
    452                 /* EOF */
    453                 break;
    454             } else {
    455                 var len, dist;
    456
    457                 j -= 256 + 1;    /* bytes + EOF */
    458                 len = readBits(cplext[j]) + cplens[j];
    459
    460                 j = bitReverse[readBits(5)]>>3;
    461                 if(cpdext[j] > 8) {
    462                     dist = readBits(8);
    463                     dist |= (readBits(cpdext[j]-8)<<8);
    464                 } else {
    465                     dist = readBits(cpdext[j]);
    466                 }
    467                 dist += cpdist[j];
    468
    469                 /*fprintf(errfp, "@%d (l%02x,d%04x)\n", SIZE, len, dist);*/
    470                 for(j=0;j<len;j++) {
    471                     var c = buf32k[(bIdx - dist) & 0x7fff];
    472                     addBuffer(c);
    473                 }
    474             }
    475             } // while
    476         } else if(type==2) {
    477             var j, n, literalCodes, distCodes, lenCodes;
    478             var ll = new Array(288+32);    // "static" just to preserve stack
    479
    480             // Dynamic Huffman tables
    481
    482             literalCodes = 257 + readBits(5);
    483             distCodes = 1 + readBits(5);
    484             lenCodes = 4 + readBits(4);
    485             //document.write("<br>param: "+literalCodes+" "+distCodes+" "+lenCodes+"<br>");
    486             for(j=0; j<19; j++) {
    487                 ll[j] = 0;
    488             }
    489
    490             // Get the decode tree code lengths
    491
    492             //document.write("<br>");
    493             for(j=0; j<lenCodes; j++) {
    494                 ll[border[j]] = readBits(3);
    495                 //document.write(ll[border[j]]+" ");
    496             }
    497             //fprintf(errfp, "\n");
    498             //document.write('<br>ll:'+ll);
    499             len = distanceTree.length;
    500             for (i=0; i<len; i++)
    501                 distanceTree[i]=new HufNode();
    502             if(CreateTree(distanceTree, 19, ll, 0)) {
    503                 flushBuffer();
    504                 return 1;
    505             }
    506             if (debug){
    507             	document.write("<br>distanceTree");
    508             	for(var a=0;a<distanceTree.length;a++){
    509                 	document.write("<br>"+distanceTree[a].b0+" "+distanceTree[a].b1+" "+distanceTree[a].jump+" "+distanceTree[a].jumppos);
    510                 	/*if (distanceTree[a].jumppos!=-1)
    511                     	document.write(" "+distanceTree[a].jump.b0+" "+distanceTree[a].jump.b1);
    512                 	*/
    513             	}
    514             }
    515             //document.write('<BR>tree created');
    516
    517             //read in literal and distance code lengths
    518             n = literalCodes + distCodes;
    519             i = 0;
    520             var z=-1;
    521             if (debug)
    522             	document.write("<br>n="+n+" bits: "+bits+"<br>");
    523             while(i < n) {
    524                 z++;
    525                 j = DecodeValue(distanceTree);
    526                 if (debug)
    527                 	document.write("<br>"+z+" i:"+i+" decode: "+j+"    bits "+bits+"<br>");
    528                 if(j<16) {    // length of code in bits (0..15)
    529                        ll[i++] = j;
    530                 } else if(j==16) {    // repeat last length 3 to 6 times
    531                        var l;
    532                     j = 3 + readBits(2);
    533                     if(i+j > n) {
    534                         flushBuffer();
    535                         return 1;
    536                     }
    537                     l = i ? ll[i-1] : 0;
    538                     while(j--) {
    539                         ll[i++] = l;
    540                     }
    541                 } else {
    542                     if(j==17) {        // 3 to 10 zero length codes
    543                         j = 3 + readBits(3);
    544                     } else {        // j == 18: 11 to 138 zero length codes
    545                         j = 11 + readBits(7);
    546                     }
    547                     if(i+j > n) {
    548                         flushBuffer();
    549                         return 1;
    550                     }
    551                     while(j--) {
    552                         ll[i++] = 0;
    553                     }
    554                 }
    555             }
    556             /*for(j=0; j<literalCodes+distCodes; j++) {
    557                 //fprintf(errfp, "%d ", ll[j]);
    558                 if ((j&7)==7)
    559                     fprintf(errfp, "\n");
    560             }
    561             fprintf(errfp, "\n");*/
    562             // Can overwrite tree decode tree as it is not used anymore
    563             len = literalTree.length;
    564             for (i=0; i<len; i++)
    565                 literalTree[i]=new HufNode();
    566             if(CreateTree(literalTree, literalCodes, ll, 0)) {
    567                 flushBuffer();
    568                 return 1;
    569             }
    570             len = literalTree.length;
    571             for (i=0; i<len; i++)
    572                 distanceTree[i]=new HufNode();
    573             var ll2 = new Array();
    574             for (i=literalCodes; i <ll.length; i++){
    575                 ll2[i-literalCodes]=ll[i];
    576             }
    577             if(CreateTree(distanceTree, distCodes, ll2, 0)) {
    578                 flushBuffer();
    579                 return 1;
    580             }
    581             if (debug)
    582            		document.write("<br>literalTree");
    583             while(1) {
    584                 j = DecodeValue(literalTree);
    585                 if(j >= 256) {        // In C64: if carry set
    586                     var len, dist;
    587                     j -= 256;
    588                     if(j == 0) {
    589                         // EOF
    590                         break;
    591                     }
    592                     j--;
    593                     len = readBits(cplext[j]) + cplens[j];
    594
    595                     j = DecodeValue(distanceTree);
    596                     if(cpdext[j] > 8) {
    597                         dist = readBits(8);
    598                         dist |= (readBits(cpdext[j]-8)<<8);
    599                     } else {
    600                         dist = readBits(cpdext[j]);
    601                     }
    602                     dist += cpdist[j];
    603                     while(len--) {
    604                         var c = buf32k[(bIdx - dist) & 0x7fff];
    605                         addBuffer(c);
    606                     }
    607                 } else {
    608                     addBuffer(j);
    609                 }
    610             }
    611         }
    612     } while(!last);
    613     flushBuffer();
    614
    615     byteAlign();
    616     return 0;
    617 };
    618
    619 JXG.Util.Unzip.prototype.unzipFile = function(name) {
    620     var i;
    621 	this.unzip();
    622 	//alert(unzipped[0][1]);
    623 	for (i=0;i<unzipped.length;i++){
    624 		if(unzipped[i][1]==name) {
    625 			return unzipped[i][0];
    626 		}
    627 	}
    628
    629   };
    630
    631
    632 JXG.Util.Unzip.prototype.unzip = function() {
    633 	//convertToByteArray(input);
    634 	if (debug)
    635 		alert(bA);
    636 	/*for (i=0;i<bA.length*8;i++){
    637 		document.write(readBit());
    638 		if ((i+1)%8==0)
    639 			document.write(" ");
    640 	}*/
    641 	/*for (i=0;i<bA.length;i++){
    642 		document.write(readByte()+" ");
    643 		if ((i+1)%8==0)
    644 			document.write(" ");
    645 	}
    646 	for (i=0;i<bA.length;i++){
    647 		document.write(bA[i]+" ");
    648 		if ((i+1)%16==0)
    649 			document.write("<br>");
    650 	}
    651 	*/
    652 	//alert(bA);
    653 	nextFile();
    654 	return unzipped;
    655   };
    656
    657  function nextFile(){
    658  	if (debug)
    659  		alert("NEXTFILE");
    660  	outputArr = [];
    661  	var tmp = [];
    662  	modeZIP = false;
    663 	tmp[0] = readByte();
    664 	tmp[1] = readByte();
    665 	if (debug)
    666 		alert("type: "+tmp[0]+" "+tmp[1]);
    667 	if (tmp[0] == parseInt("78",16) && tmp[1] == parseInt("da",16)){ //GZIP
    668 		if (debug)
    669 			alert("GEONExT-GZIP");
    670 		DeflateLoop();
    671 		if (debug)
    672 			alert(outputArr.join(''));
    673 		unzipped[files] = new Array(2);
    674     	unzipped[files][0] = outputArr.join('');
    675     	unzipped[files][1] = "geonext.gxt";
    676     	files++;
    677 	}
    678 	if (tmp[0] == parseInt("1f",16) && tmp[1] == parseInt("8b",16)){ //GZIP
    679 		if (debug)
    680 			alert("GZIP");
    681 		//DeflateLoop();
    682 		skipdir();
    683 		if (debug)
    684 			alert(outputArr.join(''));
    685 		unzipped[files] = new Array(2);
    686     	unzipped[files][0] = outputArr.join('');
    687     	unzipped[files][1] = "file";
    688     	files++;
    689 	}
    690 	if (tmp[0] == parseInt("50",16) && tmp[1] == parseInt("4b",16)){ //ZIP
    691 		modeZIP = true;
    692 		tmp[2] = readByte();
    693 		tmp[3] = readByte();
    694 		if (tmp[2] == parseInt("3",16) && tmp[3] == parseInt("4",16)){
    695 			//MODE_ZIP
    696 			tmp[0] = readByte();
    697 			tmp[1] = readByte();
    698 			if (debug)
    699 				alert("ZIP-Version: "+tmp[1]+" "+tmp[0]/10+"."+tmp[0]%10);
    700
    701 			gpflags = readByte();
    702 			gpflags |= (readByte()<<8);
    703 			if (debug)
    704 				alert("gpflags: "+gpflags);
    705
    706 			var method = readByte();
    707 			method |= (readByte()<<8);
    708 			if (debug)
    709 				alert("method: "+method);
    710
    711 			readByte();
    712 			readByte();
    713 			readByte();
    714 			readByte();
    715
    716 			var crc = readByte();
    717 			crc |= (readByte()<<8);
    718 			crc |= (readByte()<<16);
    719 			crc |= (readByte()<<24);
    720
    721 			var compSize = readByte();
    722 			compSize |= (readByte()<<8);
    723 			compSize |= (readByte()<<16);
    724 			compSize |= (readByte()<<24);
    725
    726 			var size = readByte();
    727 			size |= (readByte()<<8);
    728 			size |= (readByte()<<16);
    729 			size |= (readByte()<<24);
    730
    731 			if (debug)
    732 				alert("local CRC: "+crc+"\nlocal Size: "+size+"\nlocal CompSize: "+compSize);
    733
    734 			var filelen = readByte();
    735 			filelen |= (readByte()<<8);
    736
    737 			var extralen = readByte();
    738 			extralen |= (readByte()<<8);
    739
    740 			if (debug)
    741 				alert("filelen "+filelen);
    742 			i = 0;
    743 			nameBuf = [];
    744 			while (filelen--){
    745 				var c = readByte();
    746 				if (c == "/" | c ==":"){
    747 					i = 0;
    748 				} else if (i < NAMEMAX-1)
    749 					nameBuf[i++] = String.fromCharCode(c);
    750 			}
    751 			if (debug)
    752 				alert("nameBuf: "+nameBuf);
    753
    754 			//nameBuf[i] = "\0";
    755 			if (!fileout)
    756 				fileout = nameBuf;
    757
    758 			var i = 0;
    759 			while (i < extralen){
    760 				c = readByte();
    761 				i++;
    762 			}
    763
    764 			CRC = 0xffffffff;
    765 			SIZE = 0;
    766
    767 			if (size = 0 && fileOut.charAt(fileout.length-1)=="/"){
    768 				//skipdir
    769 				if (debug)
    770 					alert("skipdir");
    771 			}
    772 			if (method == 8){
    773 				DeflateLoop();
    774 				if (debug)
    775 					alert(outputArr.join(''));
    776 				unzipped[files] = new Array(2);
    777 				unzipped[files][0] = outputArr.join('');
    778     			unzipped[files][1] = nameBuf.join('');
    779     			files++;
    780 				//return outputArr.join('');
    781 			}
    782 			skipdir();
    783 		}
    784 	}
    785  };
    786
    787 function skipdir(){
    788     var crc,
    789         tmp = [],
    790         compSize, size, os, i, c;
    791
    792 	if ((gpflags & 8)) {
    793 		tmp[0] = readByte();
    794 		tmp[1] = readByte();
    795 		tmp[2] = readByte();
    796 		tmp[3] = readByte();
    797
    798 		if (tmp[0] == parseInt("50",16) &&
    799             tmp[1] == parseInt("4b",16) &&
    800             tmp[2] == parseInt("07",16) &&
    801             tmp[3] == parseInt("08",16))
    802         {
    803             crc = readByte();
    804             crc |= (readByte()<<8);
    805             crc |= (readByte()<<16);
    806             crc |= (readByte()<<24);
    807 		} else {
    808 			crc = tmp[0] | (tmp[1]<<8) | (tmp[2]<<16) | (tmp[3]<<24);
    809 		}
    810
    811 		compSize = readByte();
    812 		compSize |= (readByte()<<8);
    813 		compSize |= (readByte()<<16);
    814 		compSize |= (readByte()<<24);
    815
    816 		size = readByte();
    817 		size |= (readByte()<<8);
    818 		size |= (readByte()<<16);
    819 		size |= (readByte()<<24);
    820
    821 		if (debug)
    822 			alert("CRC:");
    823 	}
    824
    825 	if (modeZIP)
    826 		nextFile();
    827
    828 	tmp[0] = readByte();
    829 	if (tmp[0] != 8) {
    830 		if (debug)
    831 			alert("Unknown compression method!");
    832         return 0;
    833 	}
    834
    835 	gpflags = readByte();
    836 	if (debug){
    837 		if ((gpflags & ~(parseInt("1f",16))))
    838 			alert("Unknown flags set!");
    839 	}
    840
    841 	readByte();
    842 	readByte();
    843 	readByte();
    844 	readByte();
    845
    846 	readByte();
    847 	os = readByte();
    848
    849 	if ((gpflags & 4)){
    850 		tmp[0] = readByte();
    851 		tmp[2] = readByte();
    852 		len = tmp[0] + 256*tmp[1];
    853 		if (debug)
    854 			alert("Extra field size: "+len);
    855 		for (i=0;i<len;i++)
    856 			readByte();
    857 	}
    858
    859 	if ((gpflags & 8)){
    860 		i=0;
    861 		nameBuf=[];
    862 		while (c=readByte()){
    863 			if(c == "7" || c == ":")
    864 				i=0;
    865 			if (i<NAMEMAX-1)
    866 				nameBuf[i++] = c;
    867 		}
    868 		//nameBuf[i] = "\0";
    869 		if (debug)
    870 			alert("original file name: "+nameBuf);
    871 	}
    872
    873 	if ((gpflags & 16)){
    874 		while (c=readByte()){
    875 			//FILE COMMENT
    876 		}
    877 	}
    878
    879 	if ((gpflags & 2)){
    880 		readByte();
    881 		readByte();
    882 	}
    883
    884 	DeflateLoop();
    885
    886 	crc = readByte();
    887 	crc |= (readByte()<<8);
    888 	crc |= (readByte()<<16);
    889 	crc |= (readByte()<<24);
    890
    891 	size = readByte();
    892 	size |= (readByte()<<8);
    893 	size |= (readByte()<<16);
    894 	size |= (readByte()<<24);
    895
    896 	if (modeZIP)
    897 		nextFile();
    898
    899 };
    900
    901 };
    902
    903 /**
    904 *  Base64 encoding / decoding
    905 *  @see <a href="http://www.webtoolkit.info/">http://www.webtoolkit.info/</A>
    906 */
    907 JXG.Util.Base64 = {
    908
    909     // private property
    910     _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
    911
    912     // public method for encoding
    913     encode : function (input) {
    914         var output = [],
    915             chr1, chr2, chr3, enc1, enc2, enc3, enc4,
    916             i = 0;
    917
    918         input = JXG.Util.Base64._utf8_encode(input);
    919
    920         while (i < input.length) {
    921
    922             chr1 = input.charCodeAt(i++);
    923             chr2 = input.charCodeAt(i++);
    924             chr3 = input.charCodeAt(i++);
    925
    926             enc1 = chr1 >> 2;
    927             enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    928             enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    929             enc4 = chr3 & 63;
    930
    931             if (isNaN(chr2)) {
    932                 enc3 = enc4 = 64;
    933             } else if (isNaN(chr3)) {
    934                 enc4 = 64;
    935             }
    936
    937             output.push([this._keyStr.charAt(enc1),
    938                          this._keyStr.charAt(enc2),
    939                          this._keyStr.charAt(enc3),
    940                          this._keyStr.charAt(enc4)].join(''));
    941         }
    942
    943         return output.join('');
    944     },
    945
    946     // public method for decoding
    947     decode : function (input, utf8) {
    948         var output = [],
    949             chr1, chr2, chr3,
    950             enc1, enc2, enc3, enc4,
    951             i = 0;
    952
    953         input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    954
    955         while (i < input.length) {
    956
    957             enc1 = this._keyStr.indexOf(input.charAt(i++));
    958             enc2 = this._keyStr.indexOf(input.charAt(i++));
    959             enc3 = this._keyStr.indexOf(input.charAt(i++));
    960             enc4 = this._keyStr.indexOf(input.charAt(i++));
    961
    962             chr1 = (enc1 << 2) | (enc2 >> 4);
    963             chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    964             chr3 = ((enc3 & 3) << 6) | enc4;
    965
    966             output.push(String.fromCharCode(chr1));
    967
    968             if (enc3 != 64) {
    969                 output.push(String.fromCharCode(chr2));
    970             }
    971             if (enc4 != 64) {
    972                 output.push(String.fromCharCode(chr3));
    973             }
    974         }
    975
    976         output = output.join('');
    977
    978         if (utf8) {
    979             output = JXG.Util.Base64._utf8_decode(output);
    980         }
    981         return output;
    982
    983     },
    984
    985     // private method for UTF-8 encoding
    986     _utf8_encode : function (string) {
    987         string = string.replace(/\r\n/g,"\n");
    988         var utftext = "";
    989
    990         for (var n = 0; n < string.length; n++) {
    991
    992             var c = string.charCodeAt(n);
    993
    994             if (c < 128) {
    995                 utftext += String.fromCharCode(c);
    996             }
    997             else if((c > 127) && (c < 2048)) {
    998                 utftext += String.fromCharCode((c >> 6) | 192);
    999                 utftext += String.fromCharCode((c & 63) | 128);
    1000             }
    1001             else {
    1002                 utftext += String.fromCharCode((c >> 12) | 224);
    1003                 utftext += String.fromCharCode(((c >> 6) & 63) | 128);
    1004                 utftext += String.fromCharCode((c & 63) | 128);
    1005             }
    1006
    1007         }
    1008
    1009         return utftext;
    1010     },
    1011
    1012     // private method for UTF-8 decoding
    1013     _utf8_decode : function (utftext) {
    1014         var string = [],
    1015             i = 0,
    1016             c = 0, c2 = 0, c3 = 0;
    1017
    1018         while ( i < utftext.length ) {
    1019             c = utftext.charCodeAt(i);
    1020             if (c < 128) {
    1021                 string.push(String.fromCharCode(c));
    1022                 i++;
    1023             }
    1024             else if((c > 191) && (c < 224)) {
    1025                 c2 = utftext.charCodeAt(i+1);
    1026                 string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
    1027                 i += 2;
    1028             }
    1029             else {
    1030                 c2 = utftext.charCodeAt(i+1);
    1031                 c3 = utftext.charCodeAt(i+2);
    1032                 string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
    1033                 i += 3;
    1034             }
    1035         }
    1036         return string.join('');
    1037     },
    1038
    1039     _destrip: function (stripped, wrap){
    1040         var lines = [], lineno, i,
    1041             destripped = [];
    1042
    1043         if (wrap==null)
    1044             wrap = 76;
    1045
    1046         stripped.replace(/ /g, "");
    1047         lineno = stripped.length / wrap;
    1048         for (i = 0; i < lineno; i++)
    1049             lines[i]=stripped.substr(i * wrap, wrap);
    1050         if (lineno != stripped.length / wrap)
    1051             lines[lines.length]=stripped.substr(lineno * wrap, stripped.length-(lineno * wrap));
    1052
    1053         for (i = 0; i < lines.length; i++)
    1054             destripped.push(lines[i]);
    1055         return destripped.join('\n');
    1056     },
    1057
    1058     decodeAsArray: function (input){
    1059         var dec = this.decode(input),
    1060             ar = [], i;
    1061         for (i=0;i<dec.length;i++){
    1062             ar[i]=dec.charCodeAt(i);
    1063         }
    1064         return ar;
    1065     },
    1066
    1067     decodeGEONExT : function (input) {
    1068         return decodeAsArray(destrip(input),false);
    1069     }
    1070 };
    1071
    1072 /**
    1073  * @private
    1074  */
    1075 JXG.Util.asciiCharCodeAt = function(str,i){
    1076 	var c = str.charCodeAt(i);
    1077 	if (c>255){
    1078     	switch (c) {
    1079 			case 8364: c=128;
    1080 	    	break;
    1081 	    	case 8218: c=130;
    1082 	    	break;
    1083 	    	case 402: c=131;
    1084 	    	break;
    1085 	    	case 8222: c=132;
    1086 	    	break;
    1087 	    	case 8230: c=133;
    1088 	    	break;
    1089 	    	case 8224: c=134;
    1090 	    	break;
    1091 	    	case 8225: c=135;
    1092 	    	break;
    1093 	    	case 710: c=136;
    1094 	    	break;
    1095 	    	case 8240: c=137;
    1096 	    	break;
    1097 	    	case 352: c=138;
    1098 	    	break;
    1099 	    	case 8249: c=139;
    1100 	    	break;
    1101 	    	case 338: c=140;
    1102 	    	break;
    1103 	    	case 381: c=142;
    1104 	    	break;
    1105 	    	case 8216: c=145;
    1106 	    	break;
    1107 	    	case 8217: c=146;
    1108 	    	break;
    1109 	    	case 8220: c=147;
    1110 	    	break;
    1111 	    	case 8221: c=148;
    1112 	    	break;
    1113 	    	case 8226: c=149;
    1114 	    	break;
    1115 	    	case 8211: c=150;
    1116 	    	break;
    1117 	    	case 8212: c=151;
    1118 	    	break;
    1119 	    	case 732: c=152;
    1120 	    	break;
    1121 	    	case 8482: c=153;
    1122 	    	break;
    1123 	    	case 353: c=154;
    1124 	    	break;
    1125 	    	case 8250: c=155;
    1126 	    	break;
    1127 	    	case 339: c=156;
    1128 	    	break;
    1129 	    	case 382: c=158;
    1130 	    	break;
    1131 	    	case 376: c=159;
    1132 	    	break;
    1133 	    	default:
    1134 	    	break;
    1135 	    }
    1136 	}
    1137 	return c;
    1138 };
    1139
    1140 /**
    1141  * Decoding string into utf-8
    1142  * @param {String} string to decode
    1143  * @return {String} utf8 decoded string
    1144  */
    1145 JXG.Util.utf8Decode = function(utftext) {
    1146   var string = [];
    1147   var i = 0;
    1148   var c = 0, c1 = 0, c2 = 0, c3;
    1149   if (!JXG.exists(utftext)) return '';
    1150
    1151   while ( i < utftext.length ) {
    1152     c = utftext.charCodeAt(i);
    1153
    1154     if (c < 128) {
    1155       string.push(String.fromCharCode(c));
    1156       i++;
    1157     } else if((c > 191) && (c < 224)) {
    1158       c2 = utftext.charCodeAt(i+1);
    1159       string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
    1160       i += 2;
    1161     } else {
    1162       c2 = utftext.charCodeAt(i+1);
    1163       c3 = utftext.charCodeAt(i+2);
    1164       string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
    1165       i += 3;
    1166     }
    1167   };
    1168   return string.join('');
    1169 };
    1170
    1171 /**
    1172  * Generate a random uuid.
    1173  * http://www.broofa.com
    1174  * mailto:robert@broofa.com
    1175  *
    1176  * Copyright (c) 2010 Robert Kieffer
    1177  * Dual licensed under the MIT and GPL licenses.
    1178  *
    1179  * EXAMPLES:
    1180  *   >>> Math.uuid()
    1181  *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
    1182  */
    1183 JXG.Util.genUUID = function() {
    1184     // Private array of chars to use
    1185     var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''),
    1186         uuid = new Array(36), rnd=0, r;
    1187
    1188     for (var i = 0; i < 36; i++) {
    1189       if (i==8 || i==13 ||  i==18 || i==23) {
    1190         uuid[i] = '-';
    1191       } else if (i==14) {
    1192         uuid[i] = '4';
    1193       } else {
    1194         if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
    1195         r = rnd & 0xf;
    1196         rnd = rnd >> 4;
    1197         uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
    1198       }
    1199     }
    1200
    1201     return uuid.join('');
    1202 };