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