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 };