Pman.Gnumeric.js
[Pman.Core] / Pman.Gnumeric.js
1 //<script type="text/javascript">
2 /**
3 * @class Pman Gnumeric.
4 *-> load up a remote xml file of a gnumeric document.
5
6 * -> convert into a usable data structure
7
8 * -> ?? apply templated values ??
9 * -> allow modification of fields
10
11 * -> render to screen.
12
13 * -> send for conversion to XLS (via ssconvert)
14
15 * Usage:
16 <pre><code>
17
18     new Pman.Gnumeric( {
19       url: rootURL + '/xxx/yyy/templates/reports/myreport.xml',
20       data: { ..... },
21       listeners : {
22           load : function()
23           {
24           
25                x.applyData({ ... }); // key value data looks for {value} in strings and replaces it..
26                
27                x.set('A3', 'test');
28                
29                mypanel.update(x.toHTML());
30                
31                x.download()       
32                
33            }
34       }
35     });
36     
37
38 </code></pre>
39
40 * @constructor
41 * @param {Object} cfg   Configuration object.
42 */
43  
44
45
46 Pman.Gnumeric = function (cfg)
47 {
48     cfg.data = cfg.data || {};
49     
50     
51     
52     
53     
54     
55     this.addEvents({
56         /**
57              * @event load
58              * Fires when source document has been loaded
59              * @param {Pman.Gnumerci} this
60              */
61             "load" : true
62     }); 
63     
64     Roo.util.Observable.call(this,cfg);
65     
66     this.defaultCell = {
67         c : 0,
68         r : 0,
69         valueType : 0,
70         valueFormat : '',
71         value : '',
72         colspan: 1,
73         rowspan: 1
74           
75     };
76      
77     this.load();
78     
79    
80     
81     
82 }
83 Roo.extend(Pman.Gnumeric, Roo.util.Observable, {
84     
85     /**
86      * @cfg {String} url the source of the Gnumeric document.
87      */
88     url : false,
89       /**
90      * @cfg {Object} data overlay data for spreadsheet - from constructor.
91      */
92     data : false,
93      /**
94      * @cfg {String} downloadURL where GnumerictoExcel.php is...
95      */
96      
97     downloadURL : false,
98     
99     /**
100      * @type {XmlDocument} doc the gnumeric xml document
101      */
102     doc : false,
103     
104     /**
105      * @type {XmlNode} sheet the 'Sheet' element 
106      */
107     sheet : false,
108     
109     /**
110      * @type {XmlNode} sheet the 'Cells' element 
111      */    
112     cellholder : false,
113     /**
114      * @type {Object} grid the map[row][col] = cellData 
115      */
116     grid : false,
117     /**
118      * @type {Object} colInfo - list of column sizes
119      */
120     colInfo : false,
121     /**
122      * @type {Object} colInfoDom - column sizes dom element
123      */
124     colInfoDom : false,
125     /**
126      * @type {Object} rowInfo - list of row sizes
127      */
128     rowInfo : false,
129      /**
130      * @type {Object} rowInfoDom - dom elements with sizes
131      */
132     rowInfoDom : false,
133     /**
134      * @type {Number} cmax - maximum number of columns
135      */
136     cmax: false,
137     /**
138      * @type {Object} rmax - maximum number of rows
139      */
140     rmax : false,
141        /**
142      * @type {String} stylesheetID id of stylesheet created to render spreadsheat
143      */
144     stylesheetID : false,
145     /**
146      * @type {Number} rowOffset - used by table importer to enable multiple tables to be improted
147      */
148     
149     rowOffset : 0,
150     
151     /**
152      * load:
153      * run the connection, parse document and fire load event..
154      * can be run multiple times with new data..
155      * 
156     */
157     
158     load : function(url)
159     {
160         this.url = url || this.url;
161         if (!this.url) {
162             return;
163         }
164         // reset stufff..
165         this.doc = false;
166         this.sheet = false;
167         this.grid = false;
168         this.colInfo = false;
169         this.colInfoDom = false;
170         this.rowInfo = false;
171         this.rowInfoDom = false;
172         this.cmax = false;
173         this.rmax = false;
174         
175         if (this.stylesheetID) {
176             
177             Roo.util.CSS.removeStyleSheet(this.stylesheetID);
178             this.stylesheetID = false;
179             
180         }
181         
182         _t = this;
183         var c = new Roo.data.Connection();
184         c.request({
185             url: this.url,
186             method:  'GET',
187             success : function(resp, opts) {
188                 _t.response = resp;
189                 _t.doc = resp.responseXML;
190                 
191                 _t.parseDoc(0);
192                 
193                 
194                 _t.applyData();
195     
196                 _t.fireEvent('load', _t);
197             },
198             failure : function()
199             {
200                 Roo.MessageBox.alert("Error", "Failed to Load Template for Spreadsheet");
201             }
202         });
203         
204
205     },
206     
207     
208      
209     RCtoCell : function(r,c)
210     {
211         // we wil only support AA not AAA
212         var top = Math.floor(c/26);
213         var bot = c % 26;
214         var cc = top > 0 ? String.fromCharCode('A'.charCodeAt(0) + top) : '';
215         cc += String.fromCharCode('A'.charCodeAt(0)  + bot);
216         return cc+'' +r;
217         
218     },
219     
220     /**
221      * toRC:
222      * convert 'A1' style position to row/column reference
223      * 
224      * @arg {String} k cell name
225      * @return {Object}  as { r: {Number} , c: {Number}  }
226      */
227     
228     toRC : function(k)
229     {
230         var c = k.charCodeAt(0)-64;
231         var n = k.substring(1);
232         if (k.charCodeAt(1) > 64) {
233             c *=26;
234             c+=k.charCodeAt(1)-64;
235             n = k.substring(2);
236         }
237         return { c:c -1 ,r: (n*1)-1 }
238     },
239       /**
240      * rangeToRC:
241      * convert 'A1:B1' style position to array of row/column references
242      * 
243      * @arg {String} k cell range
244      * @return {Array}  as [ { r: {Number} , c: {Number}  }. { r: {Number} , c: {Number}  } ]
245      */
246     rangeToRC : function(s) {
247         var ar = s.split(':');
248         return [ this.toRC(ar[0]) , this.toRC(ar[1])]
249     },
250     
251     
252     
253    
254     
255     /**
256      * parseDoc:
257      * convert XML document into cells and other data..
258      * 
259      */
260     parseDoc : function(sheetnum) 
261     {
262         var _t = this;
263         this.grid = {}
264         this.rmax = 1;
265         this.cmax = 1;
266         
267         this.sheet = _t.doc.getElementsByTagNameNS('*','Sheet')[sheetnum];
268         
269         
270         this.cellholder = this.sheet.getElementsByTagNameNS('*','Cells')[0];
271         var cells = this.sheet.getElementsByTagNameNS('*','Cell');
272
273         
274         
275         Roo.each(cells, function(c) {
276            // Roo.log(c);
277             var row = c.getAttribute('Row') * 1;
278             var col = c.getAttribute('Col') * 1;
279             _t.cmax = Math.max(col+1, _t.cmax);
280             _t.rmax = Math.max(row+1, _t.rmax);
281             var vt = c.getAttribute('ValueType');
282             var vf = c.getAttribute('ValueFormat');
283             var val = c.textContent;
284             
285             if (typeof(_t.grid[row]) == 'undefined') {
286                 _t.grid[row] ={};
287             }
288             _t.grid[row][col] = Roo.applyIf({
289                 valueType : vt,
290                 valueFormat : vf,
291                 value : val,
292                 dom: c,
293                 r: row,
294                 c: col
295             }, _t.defaultCell);
296         });
297        
298         for (var r = 0; r < this.rmax;r++) {
299             if (typeof(this.grid[r]) == 'undefined') {
300               this.grid[r] ={};
301             }
302             for (var c = 0; c < this.cmax;c++) {
303                 if (typeof(this.grid[r][c]) == 'undefined') {
304                     continue;
305                 }
306                 //this.print( "[" + r + "]["+c+"]=" + grid[r][c].value +'<br/>');
307             }
308         }
309         
310         var merge = this.sheet.getElementsByTagNameNS('*','Merge');
311
312         Roo.each(merge, function(c) {
313             var rc = _t.rangeToRC(c.textContent);
314             //Roo.log(JSON.stringify(rc))
315             if (typeof(_t.grid[rc[0].r][rc[0].c]) == 'undefined') {
316                 _t.grid[rc[0].r][rc[0].c] =  Roo.applyIf({ r : rc[0].r, c : rc[0].c }, _t.defaultCell);
317             }
318                 
319             _t.grid[rc[0].r][rc[0].c].colspan = (rc[1].c - rc[0].c) + 1;
320             _t.grid[rc[0].r][rc[0].c].rowspan = (rc[1].r - rc[0].r) + 1;
321             for(var r = (rc[0].r); r < (rc[1].r+1); r++) {
322                for(var c = rc[0].c; c < (rc[1].c+1); c++) {
323                     //Roo.log('adding alias : ' + r+','+c);
324                    _t.grid[r][c] = _t.grid[rc[0].r][rc[0].c];
325                }
326            }
327             
328             
329         });
330         // read colinfo..
331         var ci = this.sheet.getElementsByTagNameNS('*','ColInfo');
332         this.colInfo = {};
333         this.colInfoDom = {};
334         
335         Roo.each(ci, function(c) {
336             var count = c.getAttribute('Count') || 1;
337             var s =  c.getAttribute('No')*1;
338             for(var i =0; i < count; i++) {
339                 _t.colInfo[s+i] = Math.floor(c.getAttribute('Unit')*1);
340                 _t.colInfoDom[s+i] = c;
341             }
342         });
343         
344         
345         ci = this.sheet.getElementsByTagNameNS('*','RowInfo');
346         
347         this.rowInfo = {};
348         this.rowInfoDom = {};
349         Roo.each(ci, function(c) {
350             var count = c.getAttribute('Count') || 1;
351             var s =  c.getAttribute('No')*1;
352             for(var i =0; i < count; i++) {
353                 _t.rowInfoDom[s+i] = c;
354                 _t.rowInfo[s+i] = Math.floor(c.getAttribute('Unit')*1);
355             }
356         });
357     
358         _t.parseStyles();
359         _t.overlayStyles();
360                 
361         
362      
363         
364     },
365      /**
366      * overlayStyles:
367      * put the style info onto the cell data.
368      * 
369      */
370     overlayStyles : function ()
371     {
372            // apply styles.
373         var _t = this;
374         Roo.each(this.styles, function(s) {
375        
376             for (var r = s.r; r < s.r1;r++) {
377                 if (typeof(_t.grid[r]) == 'undefined') {
378                    continue;
379                 }
380                 for (var c = s.c; c < s.c1;c++) {
381                     if (c > _t.cmax) continue;
382     
383                     if (typeof(_t.grid[r][c]) == 'undefined') _t.grid[r][c] = Roo.applyIf({ r: r , c : c }, _t.defaultCell);
384                     var g=_t.grid[r][c];
385                     if (typeof(g.cls) =='undefined') {
386                         g.cls = [];
387                         g.styles = [];
388                     }
389                     if (g.cls.indexOf(s.name)  > -1) continue;
390                     g.cls.push(s.name);
391                     g.styles.push(s.dom);
392                     
393                 }
394             }
395         });
396     },
397      /**
398      * parseStyles: 
399      *  read the style information
400      * generates a stylesheet for the current file
401      * this should be disposed of really.....
402      * 
403      */
404     parseStyles : function() {
405                 
406         var srs = this.sheet.getElementsByTagNameNS('*','StyleRegion');
407         var _t  = this;
408         var ent = {};
409         
410         var map =  {
411             HAlign : function(ent,v) { 
412                 ent['text-align'] = { '1' : 'left', '8': 'center', '4' : 'right'}[v] || 'left';
413             },
414             VAlign : function(ent,v) { 
415                 ent['vertical-align'] = { '1' : 'top', '4': 'middle', '8' : 'bottom'}[v]  || 'top'
416             },
417             Fore : function(ent,v) { 
418                 var col=[];
419                 Roo.each(v.split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
420                 ent['color'] = 'rgb(' + col.join(',') + ')';
421             },
422             Back : function(ent,v) { 
423                 var col=[];
424                 Roo.each(v.split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
425                 ent['background-color'] = 'rgb(' + col.join(',') + ')';
426             },
427             FontUnit : function(ent,v) { 
428                 ent['font-size'] = v + 'px';
429             },
430             FontBold : function(ent,v) { 
431                 if (v*1 < 1) return;
432                 ent['font-weight'] = 'bold';
433             },
434             FontItalic : function(ent,v) { 
435                 if (v*0 < 1) return;
436                 //ent['font-weight'] = 'bold';
437             },
438             FontName : function(ent,v) { 
439                 ent['font-family'] = v;
440             },
441             BorderStyle : function(ent,v) { 
442                 var vv  = v.split('-');
443                 ent['border-'+vv[0]+'-style'] = 'solid';
444                 ent['border-'+vv[0]+'-width'] = vv[1]+'px';
445             },
446             BorderColor : function(ent,v) { 
447                 var vv  = v.split('-');
448                 var col=[];
449                 Roo.each(vv[1].split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
450                 ent['border-'+vv[0]+'-color'] = 'rgb(' + col.join(',') + ')';
451             }
452         }
453         function add(e, k, v) {
454             //Roo.log(k,v);
455             e.gstyle[k] = v;
456             if (typeof(map[k]) == 'undefined') {
457                 return;
458             }
459             map[k](e.style,v);    
460         }
461         var css = {};
462         var styles = [];
463         var sid= Roo.id();
464         
465         
466         Roo.each(srs, function(sr,n)
467         {
468             ent = {
469                 c : sr.getAttribute('startCol') *1,
470                 r : sr.getAttribute('startRow')*1,
471                 c1 : (sr.getAttribute('endCol')*1) +1,
472                 r1 : (sr.getAttribute('endRow')*1) +1,
473                 style : {},  // key val of style for HTML..
474                 gstyle : {}, // key val of attributes used..
475                 name : sid +'-gstyle-' + n,
476                 dom : sr
477                 
478             };
479     
480             Roo.each(sr.getElementsByTagNameNS('*','Style')[0].attributes, function(e) { 
481                 add(ent, e.name, e.value);
482             });
483             if (sr.getElementsByTagNameNS('*','Font').length) {
484                 Roo.each(sr.getElementsByTagNameNS('*','Font')[0].attributes, function(e) { 
485                      add(ent, 'Font'+e.name, e.value);
486     
487                 });
488                 add(ent, 'FontName', sr.getElementsByTagNameNS('*','Font')[0].textContent);
489     
490             }
491             if (sr.getElementsByTagNameNS('*','StyleBorder').length) {
492                 Roo.each(sr.getElementsByTagNameNS('*','StyleBorder')[0].childNodes, function(e) {
493                     if (!e.tagName) {
494                         return;
495                     }
496                     Roo.each(e.attributes, function(ea) { 
497                         add(ent, 'Border'+ea.name, e.tagName.split(':')[1].toLowerCase() + '-' + ea.value);
498                     });
499                 })
500                     
501             }
502             styles.push(ent);
503             css['.'+ent.name] = ent.style;
504         });
505         
506         this.styles = styles;
507         
508         this.stylesheetID = sid;
509         Roo.util.CSS.createStyleSheet(css, sid);
510     },
511
512     
513     
514     
515     /* ---------------------------------------  AFTER LOAD METHODS... ----------------------- */
516     /**
517      * set: 
518      * Set the value of a cell..
519      * @param {String} cell name of cell, eg. C10 or { c: 1, r :1 }
520          
521      * @param {Value} value to put in cell..
522      * @param {ValueType} type of value
523      * @param {ValueFormat} value format of cell
524      * 
525      * Cells should exist at present, we do not make them up...
526      */
527      
528     
529     set : function(cell, v, vt, vf) {
530         
531         var cs= typeof(cell) == 'string' ? this.toRC(cell) : cell;
532         //Roo.log( cs.r+ ',' + cs.c + ' = '+ v);
533         // need to generate clell if it doe
534         if (typeof(this.grid[cs.r]) == 'undefined') {
535             Roo.log('no row:' + cell);
536             this.grid[cs.r] = []; // create a row..
537             //return;
538         }
539         if (typeof(this.grid[cs.r][cs.c]) == 'undefined') {
540             Roo.log('cell not defined:' + cell);
541             this.createCell(cs.r,cs.c);
542         }
543         if (typeof(this.grid[cs.r][cs.c].dom) == 'undefined') {
544             Roo.log('no default content for cell:' + cell);
545             this.createCell(cs.r,cs.c);
546             //return;
547         }
548         this.grid[cs.r][cs.c].value=  v;
549         this.grid[cs.r][cs.c].dom.textContent=  v;
550         if (typeof(vt) != 'undefined') {
551             this.grid[cs.r][cs.c].valueType = vt;
552             this.grid[cs.r][cs.c].dom.setAttribute('ValueType', vt);
553             if (vt === '' || vt === false) { // value type is empty for formula's
554                 this.grid[cs.r][cs.c].dom.removeAttribute('ValueType');
555             }
556         }
557         if (typeof(vf) != 'undefined' && vf !== false) {
558             this.grid[cs.r][cs.c].valueFormat = vf;
559             this.grid[cs.r][cs.c].dom.setAttribute('ValueFormat', vf);
560             if (vf === '' || vf === false) { // value type is empty for formula's
561                 this.grid[cs.r][cs.c].dom.removeAttribute('ValueFormat');
562             }
563         }
564         
565     },
566     
567     // private
568     copyRow : function(src, dest) {
569         if (dest == src) {
570             return;
571         }
572        // Roo.log('create Row' + dest);
573         if (typeof(this.grid[dest]) == 'undefined') {
574             this.grid[dest] = {}
575         }
576         
577            
578         for (var c = 0; c < this.cmax; c++) {
579
580             this.copyCell({ r: src, c: c } , { r: dest, c: c});
581             
582         }
583         this.rmax = Math.max(this.rmax, dest +1);
584         
585     },
586     
587     // private
588     
589     createCell: function(r,c)
590     {
591         //<gnm:Cell Row="6" Col="5" ValueType="60">Updated</gnm:Cell>    
592         var nc = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:Cell');
593         this.cellholder.appendChild(nc);
594         var lb = this.doc.createTextNode("\n");// add a line break..
595         this.cellholder.appendChild(lb);
596         
597         nc.setAttribute('Row', new String(r));
598         nc.setAttribute('Col', new String(c));
599         nc.setAttribute('ValueType', '60');
600         nc.textContent = '';
601         
602         this.grid[r][c] = Roo.applyIf({
603             valueType : '60',
604             valueFormat : '',
605             value : '',
606             dom: nc,
607             r: r,
608             c: c
609             }, _t.defaultCell);
610         
611         return nc;
612
613     },
614     
615     // private
616     copyCell : function(src, dest)
617     {
618         var old = this.grid[src.r][src.c];
619         // is it an alias...
620         if ((old.c != src.c)  || (old.r != src.r)) {
621             // only really works on horizonatal merges..
622             
623             this.grid[dest.r][dest.c] = this.grid[desc.r][old.c]; // let's hope it exists.
624             return;
625         }
626         
627         
628         var nc = Roo.apply({}, this.grid[src.r][src.c]);
629         
630         nc.value = '';
631         if (typeof(old.dom) == 'undefined') {
632             Roo.log("No cell to copy for " + Roo.encode(src));
633             return;
634         }
635         this.grid[dest.r][dest.c] = nc;
636         nc.dom = old.dom.cloneNode(true);
637         nc.dom.setAttribute('Row', dest.r);
638         nc.dom.setAttribute('Cell', dest.c);
639         nc.dom.textContent = '';
640         old.dom.parentNode.appendChild(nc.dom);
641         if (!old.styles || !old.styles.length) {
642             return;
643         }
644         //Roo.log("DEST");
645         //Roo.log(dest);
646         //Roo.log("STYLES");
647         //  .styles...
648         Roo.each(old.styles, function(s) {
649             // try and extend existing styles..
650             var er = s.getAttribute('endRow') * 1;
651             var ec = s.getAttribute('endCol') * 1;
652             //Roo.log(s);
653             if (dest.r == er) {
654                 s.setAttribute('endRow', dest.r + 1);
655             }
656             if (dest.c == ec) {
657                 s.setAttribute('endCol', dest.c + 1);
658             }
659             /*var ns = s.cloneNode(true);
660             s.parentNode.appendChild(ns);
661             ns.setAttribute('startCol', dest.c);
662             ns.setAttribute('startRow', dest.r);
663             ns.setAttribute('endCol', dest.c + 1);
664             ns.setAttribute('endRow', dest.r +1);
665             */
666         });
667         
668     },
669     
670     
671     /**
672      * applyData: 
673      * Set the value of a cell..
674      * @param {String} cell name of cell, eg. C10
675      * @param {Value} value to put in cell..
676      * 
677      * Cells should exist at present, we do not make them up...
678      */
679      
680     applyData : function(data)
681     {
682         
683         data = data || this.data;
684         for (var r = 0; r < this.rmax;r++) {
685             if (typeof(this.grid[r]) == 'undefined') continue;
686             for (var c = 0; c < this.cmax;c++) {  
687                 if (typeof(this.grid[r][c]) == 'undefined') {
688                     continue;
689                 }
690                 if (!this.grid[r][c].value.length 
691                         || !this.grid[r][c].value.match(/\{/)) {
692                     continue;
693                 }
694                 
695                 var x = new Roo.Template({ html: this.grid[r][c].value });
696                 try {
697                     var res = x.applyTemplate(data);
698                     //Roo.log("set " + r  + "," + c + ":"+res)
699                     this.set({ r: r, c: c}, x.applyTemplate(data));
700                 } catch (e) {
701                  //   Roo.log(e.toString());
702                   //  Roo.log(e);
703                     // continue?
704                 }
705                 
706             }
707         }
708             
709     },
710     
711     readTableData : function(table)
712     {
713         // read the first row.
714         var tds = Roo.get(table).select('tr').item(0).select('td');
715         var nc = 0;
716         tds.each(function(td) {
717             var cs = td.dom.getAttribute('colspan');
718             cs = cs ? cs * 1 : 1;
719             nc += cs;
720         });
721         var tr = document.createElement('tr');
722         table.appendChild(tr);
723         var ar = {}
724         for (i =0; i < nc; i++) {
725             ar[i] = document.createElement('td');
726             tr.appendChild(ar[i]);
727         }
728         // find the left.
729         var ret = { cols : nc, pos : {} };
730         for (i =0; i < nc; i++) {
731             ret.pos[ Roo.get(ar[i]).getLeft()] =i;
732         }
733         table.removeChild(tr);
734         return ret;
735     },
736     
737      
738    
739      
740     /**
741      * importTable: 
742      * Import a table and put it into the spreadsheet
743      * @param {HTMLTable} datagrid dom element of html table.
744      * @param {Number} xoff X offset to start rendering to
745      * @param {Number} yoff Y offset to start rendering to
746      **/
747      
748  
749     importTable : function (datagrid, xoff,yoff)
750     {
751         if (!datagrid) {
752             Roo.log("Error table not found!?");
753             return;
754         }
755         xoff = xoff || 0;
756         yoff = yoff || 0;
757         
758         
759         var table_data = this.readTableData(datagrid);
760         
761         var cleanHTML = function (str) {
762             
763              var ret = str;
764             ret = ret.replace(/&nbsp;/g,'.');
765             ret = ret.replace(/\n/g,'.');
766             ret = ret.replace(/\r/g,'.');
767             var i;
768             while (-1 != (i = ret.indexOf(unescape('%A0')))) {
769                 ret = ret.substring(0,i) + ' ' + ret.substring(i+1,str.length);
770             }
771             return ret;
772         };
773
774         
775         // <cell col="A" row="1">Test< / cell>
776         // <cell col="B" row="2" type="Number" format="test1">30< / cell>
777         var rowOffsets = {};
778         var rows = datagrid.getElementsByTagName('tr');
779         //alert(rows.length);
780         
781         
782         for(var row=0;row<rows.length;row++) {
783             
784             // let's see what affect this has..
785             // it might mess things up..
786             
787             if (rows[row].getAttribute('xls:height')) {
788                 this.setRowHeight(row + yoff, 1* rows[row].getAttribute('xls:height'));
789             } else {
790                 this.setRowHeight( row + yoff, Roo.get(rows[row]).getHeight());
791             }
792             
793          
794             var cols = rows[row].getElementsByTagName('td');
795             
796             
797             for(var col=0;col < cols.length; col++) {
798                 
799                 
800                
801                 
802                 var colspan = cols[col].getAttribute('colspan');
803                 colspan  = colspan ? colspan *1 : 1;
804                 
805                 var rowspan = cols[col].getAttribute('rowspan');
806                 rowspan = rowspan ? rowspan * 1 : 1;
807                 
808                 var realcol = table_data.pos[ Roo.get(cols[col]).getLeft() ];
809                 
810                 
811                 
812                 if (colspan > 1 || rowspan > 1) {
813                     
814                     // getting thisese right is tricky..
815                     this.mergeRegion(
816                         realcol + xoff,
817                         row + yoff +1,
818                         realcol+ xoff + (colspan -1),
819                         row + yoff + rowspan 
820                     );
821                     
822                 }
823                 
824                 // skip blank cells
825                 // set the style first..
826                 this.parseHtmlStyle( cols[col], row + yoff, realcol + xoff   , colspan, rowspan);
827                 
828                 if (!cols[col].childNodes.length) {
829                      
830                     continue;
831                 }
832                 
833                 
834                 
835                 
836                 var vt = '60';
837                 var vf = false;
838                 var xlstype = cols[col].getAttribute('xls:type');
839                 switch(xlstype) {
840                     case 'int':
841                         vt = 30; // int!!!!
842                     
843                         break;
844                         
845                     case 'float':
846                         vt = 40; // float!!!!
847                         if (cols[col].getAttribute('xls:floatformat')) {
848                             vf = cols[col].getAttribute('xls:floatformat');
849                         }
850                         break;
851                         
852                     case 'date':
853                         vt = 30;
854                         //ValueFormat="d/m/yyyy" 38635  
855                         var vf = 'd/m/yyy';
856                         if (cols[col].getAttribute('xls:dateformat')) {
857                             vf= cols[col].getAttribute('xls:dateformat');
858                         }
859                         
860                        
861                         
862                         break;
863                     
864                     default:
865                        
866                         break;
867                 }
868                
869                 if (!cols[col].childNodes[0].nodeValue) {
870                    
871                     continue;
872                 }
873                 if (!cols[col].childNodes[0].nodeValue.replace(/^\s*|\s*$/g,"").length) {
874                   
875                     continue;
876                 }
877                 // strip me.!
878                 var cell_value_text = cleanHTML(cols[col].childNodes[0].nodeValue);
879        
880                 if (cols[col].getAttribute('xls:percent')) {
881                     cell_value_text = '' + ((cell_value_text * 1) / 100);
882                 }
883
884                 if (cell_value_text.length && (vt == 30) && xlstype == 'date') {
885                     var bits = cell_value_text.split(/-/);
886                     var cur = new Date(bits[0],bits[1]-1,bits[2]);
887                     cell_value_text = '' + Math.round((cur.getTime() - Date.UTC(1899,11,30)) / (24 * 60 * 60 * 1000));
888                 }
889
890                 
891                 
892                 if (cols[col].getAttribute('xls:formula')) {
893                     var s = cols[col].getAttribute('xls:formula');
894                     vt = '';
895                     cell_value_text = s.replace(/#row#/g,(row + yoff + 1));
896                 }
897                 this.set({ r: row + yoff, c : realcol + xoff }, cell_value_text, vt, vf);
898                  
899                   
900                 
901                 
902                 
903             }
904         }
905         this.rowOffset += rows.length;
906         
907     },
908     
909     
910     
911     parseHtmlStyle : function(dom, row, col, colspan, rowspan) {
912         
913         function toCol (rgb) {
914             
915             var ar = rgb.replace(/rgb[a]?\(/, '').replace(/\)/, '').replace(/ /, '').split(',');
916             var rcs = [];
917             ar = ar.slice(0,3);
918             Roo.each(ar, function(c) { 
919                 rcs.push((c*256).toString(16)) ; 
920             });
921             return rcs.join(':');
922             
923         }
924         
925         var el = Roo.get(dom);
926         var map =  {
927             'text-align'  : function(ent,v) { 
928                 ent['HAlign'] = { 'left' : '1', 'center' : '8' ,  'right' : '4' }[v] || '1';
929             },
930             'vertical-align': function(ent,v) { 
931                 ent['VAlign'] = { 'top' : '1', 'middel' : '8' ,  'bottom' : '4' }[v] || '1';
932             },
933             
934             'color': function(ent,v) { 
935                 ent['Fore'] = toCol(v);
936             },
937             'background-color' : function(ent,v) { 
938                 ent['Back'] = toCol(v);
939                 
940             }
941             
942         }
943        
944         var ent = {
945                 HAlign:"1",
946                 VAlign:"2",
947                 WrapText:"0",
948                 ShrinkToFit:"0",
949                 Rotation:"0",
950                 Shade:"0",
951                 Indent:"0",
952                 Locked:"0",
953                 Hidden:"0",
954                 Fore:"0:0:0",
955                 Back:"FFFF:FFFF:FFFF",
956                 PatternColor:"0:0:0",
957                 Format:"General"
958         };
959            
960         for(var k in map) {
961             var val = el.getStyle(k);
962             if (!val || !val.length) {
963                continue;
964             }
965             map[k](ent,val);
966         }
967         // fonts..
968         var fmap = {
969             
970            
971             'font-size' : function(ent,v) { 
972                 ent['Unit'] = v.replace(/px/, '');
973             },
974             'font-weight' : function(ent,v) { 
975                 if (v != 'bold') return;
976                 ent['Bold'] = 1;
977             } 
978             //FontItalic : function(ent,v) { 
979             //    if (v*0 < 1) return;
980             //    //ent['font-weight'] = 'bold';
981             //},
982             
983         }
984        
985         var fent = {
986             Unit:"10",
987             Bold:"0",
988             Italic:"0",
989             Underline:"0",
990             StrikeThrough:"0"
991         };
992         
993         for(var k in fmap) {
994             var val = el.getStyle(k);
995             if (!val || !val.length) {
996                continue;
997             }
998             fmap[k](fent,val);
999         }
1000         var font = el.getStyle('font-family') || 'Sans';
1001         if (font.split(',').length > 1) {
1002             font = font.split(',')[1].replace(/\s+/, '');
1003         }
1004         
1005         
1006         /// -- now create elements..
1007         
1008         var objs = this.sheet.getElementsByTagNameNS('*','Styles')[0];
1009         
1010         //<gnm:StyleRegion startCol="0" startRow="0" endCol="255" endRow="65535"
1011         var sr = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:StyleRegion');
1012         objs.appendChild(sr);
1013         objs.appendChild(this.doc.createTextNode("\n"));// add a line break..
1014
1015         sr.setAttribute('startCol', col);
1016         sr.setAttribute('endCol', col+ colspan-1);
1017         sr.setAttribute('startRow', row);
1018         sr.setAttribute('endRow', row + rowspan -1);
1019         
1020         
1021         var st = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:Style');
1022         sr.appendChild(st);
1023         // do we need some defaults..
1024         for(var k in ent) {
1025             Roo.log(k);
1026             st.setAttribute(k, ent[k]);
1027         }
1028         
1029         var fo = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:Font');
1030         st.appendChild(fo);
1031         // do we need some defaults..
1032         for(var k in fent) {
1033             fo.setAttribute(k, fent[k]);
1034         }
1035         fo.textContent  = font;
1036         
1037         var sb = false;
1038         // borders..
1039         Roo.each(['top','left','bottom','right'], function(p) {
1040             var w = el.getStyle('border-' + p + '-width').replace(/px/, '');
1041             if (!w || !w.length || (w*1) < 1) {
1042                 return;
1043             }
1044             if (!sb) {
1045                 sb= this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:StyleBorder');
1046             }
1047             var be = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:' + p[0].toUpperCase() + p.substring(1));
1048             be.setAttribute('Style', '1');
1049             be.setAttribute('Color', '0:0:0'); // fixme..
1050             sb.appendChild(be);
1051             
1052         }, this);
1053         // start adding them all together..
1054         
1055         if (sb) {
1056             st.appendChild(sb)
1057         }
1058         
1059         
1060         
1061         
1062     },
1063     
1064     
1065     
1066     /**
1067      * writeImage:
1068      * write an image (needs base64 data to write it)
1069      * 
1070      * 
1071      * @param {Number} row  row to put it in (rows start at 0)
1072      * @param {Number} col  column to put it in
1073      * @param {Number} data  the base64 description of the images
1074      * @param {Number} width image width
1075      * @param {Number} width image height
1076      * 
1077      */
1078     
1079     
1080     writeImage : function (row, col, data, width, height) 
1081     {
1082         
1083         // our default height width is 50/50 ?!
1084         //console.log('w='+width+',height='+height);
1085                 //        <gmr:Objects>
1086         row*=1;
1087         col*=1;
1088         height*=1;
1089         width*=1;
1090         var objs = this.sheet.getElementsByTagNameNS('*','Objects')[0];
1091         var soi = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:SheetObjectImage');
1092         
1093         //<gmr:SheetObjectImage 
1094         //      ObjectBound="A3:J8" 
1095         //      ObjectOffset="0.375 0.882 0.391 0.294" 
1096         //      ObjectAnchorType="16 16 16 16" 
1097         //      Direction="17" 
1098         //      crop-top="0.000000" 
1099         //      crop-bottom="0.000000" 
1100         //      crop-left="0.000000" 
1101         //      crop-right="0.000000">
1102                 
1103                 
1104         //alert(gnumeric_colRowToName(row,col));
1105                
1106         // this is where we really have fun!!!... 
1107         // since our design currently assumes the height is enough to fit
1108         // stuff in, we only really need to work out how wide it has to be..
1109         
1110         // note we should probably use centralized calcs if it fits in the first cell!
1111         
1112         // step 1 - work out how many columns it will span..
1113         // lets hope the spreadsheet is big enought..
1114         var colwidth = 0;
1115         var endcol=col
1116         for ( endcol=col;endcol <100; endcol++) {
1117             if (!this.colInfo[endcol]) {
1118                 this.colInfo[endcol] = 100; // eak fudge
1119             }
1120             colwidth += this.colInfo[endcol];
1121             if (colwidth > width) {
1122                 break;
1123             }
1124         }
1125        
1126         
1127         soi.setAttribute('ObjectBound',
1128             //gnumeric_colRowToName(row,col) + ':' + gnumeric_colRowToName(row+1,col+1));
1129             this.RCtoCell(row,col) + ':' + this.RCtoCell(row,endcol));
1130      
1131         var ww = 0.01; // offset a bit...
1132         var hh = 0.01; //
1133         
1134         var ww2 = 1 - ((colwidth - width) / this.colInfo[endcol]);
1135         var hh2 = 0.99;
1136         
1137         var offset_str = ww + ' '  + hh + ' ' + ww2 + ' '+hh2;
1138         //console.log(offset_str );
1139         //alert(offset_str);
1140         soi.setAttribute('ObjectOffset', offset_str);
1141         soi.setAttribute('ObjectAnchorType','16 16 16 16');
1142         soi.setAttribute('Direction','17');
1143         soi.setAttribute('crop-top','0.000000');
1144         soi.setAttribute('crop-bottom','0.000000');
1145         soi.setAttribute('crop-left','0.000000');
1146         soi.setAttribute('crop-right','0.000000');
1147                 // <Content image-type="jpeg" size-bytes="3900">......  < / Content>
1148         var content = this.doc.createElement('Content');
1149         content.setAttribute('image-type','jpeg');
1150         //alert(imgsrc);
1151         
1152         content.setAttribute('size-bytes',data.length);
1153         content.textContent = data;
1154         soi.appendChild(content);
1155         objs.appendChild(soi);
1156         return true;
1157                 //< /gnm:SheetObjectImage>
1158                 // < /gnm:Objects>
1159
1160     },
1161  
1162     /**
1163      * mergeRegion:
1164      * Merge cells in the spreadsheet. (does not check if existing merges exist..)
1165      * 
1166      * @param {Number} col1  first column 
1167      * @param {Number} row1  first row
1168      * @param {Number} col2  to column 
1169      * @param {Number} row2  to row
1170      * 
1171      */
1172     mergeRegion : function (col1,row1,col2,row2)
1173     {
1174         var cell = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:Merge');
1175         //if (col1 > 50|| col2 > 50) { // do not merge cols off to right?
1176        //     return;
1177         //}
1178         
1179         cell.textContent = this.RCtoCell(row1,col1) + ':' + this.RCtoCell(row2,col2)
1180         
1181         //var merges = this.gnumeric.getElementsByTagNameNS('*','MergedRegions');
1182         var merges = this.sheet.getElementsByTagNameNS('*','MergedRegions');
1183         if (!merges || !merges.length) {
1184             merges = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd','gnm:MergedRegions');
1185             var sl = this.sheet.getElementsByTagNameNS('*','SheetLayout')[0];
1186             this.sheet.insertBefore(merges,sl);
1187         } else {
1188             merges = merges[0];
1189            }
1190         merges.appendChild(cell);
1191     
1192     },
1193     /**
1194      * setRowHeight:
1195      * Sets the height of a row.
1196      * 
1197      * @param {Number} r  the row to set the height of. (rows start at 0)
1198      * @param {Number} height (in pixels)
1199      */
1200     setRowHeight : function (r,height)
1201     {
1202         
1203         //<gmr:Rows DefaultSizePts="12.75">
1204         //   <gmr:RowInfo No="2" Unit="38.25" MarginA="0" MarginB="0" HardSize="1"/>
1205     //  < /gmr:Rows>
1206     
1207         if (this.rowInfoEl[r]) {
1208             this.rowInfoEl[r].setAttribute('Unit', height);
1209             return;
1210         }
1211     
1212         var rows = this.sheet.getElementsByTagNameNS('*','Rows')[0]; // assume this exists..
1213         var ri = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd','gnm:RowInfo');
1214         // assume we have no rows..
1215         ri.setAttribute('No', r-1);
1216         ri.setAttribute('Unit', height);
1217         ri.setAttribute('MarginA', 0);
1218         ri.setAttribute('MarginB', 0);
1219         ri.setAttribute('HardSize', 1);
1220         rows.appendChild(ri);
1221         this.rowInfoEl[r] = ri;
1222     },
1223      
1224     /**
1225      * setSheetName: 
1226      * Set the sheet name.
1227      * @param {String} title for sheet
1228      **/
1229     setSheetName : function(name,sheet)
1230     {
1231         sheet = sheet || 0;
1232         /*
1233         <gnm:SheetNameIndex>
1234         <gnm:SheetName>Sheet1</gnm:SheetName>
1235         <gnm:SheetName>Sheet2</gnm:SheetName>
1236         <gnm:SheetName>Sheet3</gnm:SheetName>
1237         </gnm:SheetNameIndex>
1238         */
1239         // has to set sheet name on index and body..
1240         Roo.log(sheet);
1241         Roo.log(name);
1242         var sheetnames = this.doc.getElementsByTagNameNS('*','SheetName');
1243         if (sheet >=  sheetnames.length) {
1244             
1245             sheetnames[0].parentNode.appendChild(sheetnames[sheetnames.length-1].cloneNode(true));
1246             // copy body.
1247             sheetnames = this.doc.getElementsByTagNameNS('*','Sheet');
1248             sheetnames[0].parentNode.appendChild(sheetnames[sheetnames.length-1].cloneNode(true));
1249             var sn = this.doc.getElementsByTagNameNS('*','Sheet')[sheet];
1250             var cls = sn.getElementsByTagNameNS('*','Cells')[0]
1251             while (cls.childNodes.length) {
1252                 cls.removeChild(cls.firstChild);
1253             }
1254             
1255         }
1256         
1257         var sheetn = this.doc.getElementsByTagNameNS('*','SheetName')[sheet];
1258         sheetn.textContent = name;
1259         var sheetb = this.doc.getElementsByTagNameNS('*','Sheet')[sheet].getElementsByTagNameNS('*','Name')[0];
1260         sheetb.textContent = name;
1261         this.parseDoc(sheet);
1262         
1263         
1264         
1265         
1266     },
1267      /**
1268      * setColumnWidth: 
1269      * Set the column width
1270      * @param {Number} column number (starts at '0')
1271      * @param {Number} width size of column
1272      **/
1273     setColumnWidth : function(column, width)
1274     {
1275         column = column *1; 
1276         width= width*1;
1277         if (typeof(this.colInfoDom[column]) == 'undefined') {
1278             var cols = this.doc.getElementsByTagNameNS('*','Cols')[0];
1279             var ri = this.doc.createElementNS('http://www.gnumeric.org/v10.dtd', 'gnm:ColInfo');
1280             ri.setAttribute('No', column);
1281             ri.setAttribute('Unit', width);
1282             ri.setAttribute('MarginA', 2);
1283             ri.setAttribute('MarginB', 2);
1284             ri.setAttribute('HardSize', 1);
1285             cols.appendChild(ri);
1286             this.colInfo[column] = width;
1287             this.colInfoDom[column]  = ri;
1288             return;
1289         }
1290         this.colInfoDom[column].setAttribute('Unit', width);
1291         
1292     },
1293     
1294     
1295     
1296     
1297     
1298      /**
1299      * toHTML: 
1300      * Convert spreadsheet into a HTML table.
1301      */
1302             
1303     toHTML :function()
1304     {
1305          var _t = this;
1306         function calcWidth(sc, span)
1307         {
1308             var n =0;
1309             for(var i =sc; i< sc+span;i++) {
1310                 n+=_t.colInfo[i];
1311             }   
1312             return n;
1313         }
1314         
1315         var grid = this.grid;
1316         // lets do a basic dump..
1317         var out = '<table style="table-layout:fixed;" cellpadding="0" cellspacing="0">';
1318         for (var r = 0; r < this.rmax;r++) {
1319             out += '<tr style="height:'+this.rowInfo[r]+'px;">';
1320             for (var c = 0; c < this.cmax;c++) {
1321                 var g = (typeof(grid[r][c]) == 'undefined') ? defaultCell  : grid[r][c];
1322                 
1323                 if (typeof(g.cls) =='undefined') g.cls = [];
1324                 var w= calcWidth(c,g.colspan);
1325                 out+=String.format('<td colspan="{0}" rowspan="{1}"  class="{4}"><div style="{3}">{2}</div></td>', 
1326                     g.colspan, g.rowspan, g.value,
1327                     'overflow:hidden;' + 
1328                     'width:'+w+'px;' +
1329                    
1330                     'text-overflow:ellipsis;' +
1331                     'white-space:nowrap;',
1332                      g.cls.join(' ')
1333     
1334     
1335                 );
1336                 c+=(g.colspan-1);
1337             }
1338             out += '</tr>';
1339         }
1340         //Roo.log(out);
1341         return out+'</table>';
1342         
1343         
1344         
1345     },
1346     /**
1347      * download:
1348      * @param {String} name  filename to downlaod (without xls)
1349      * @param {String} callback  (optional) - callback to call after callback is complete.
1350      */
1351     download : function(name,callback)
1352     {
1353         name = name || "Missing_download_filename";
1354         
1355         if (this.downloadURL && this.downloadURL.charAt(this.downloadURL .length-1) != '/') {
1356             this.downloadURL += '/';
1357         }
1358         
1359         var ser = new XMLSerializer();
1360         var x = new Pman.Download({
1361             method: 'POST',
1362             params : {
1363                xml : ser.serializeToString(this.doc),
1364                format : 'xls', //xml
1365                debug : 0
1366                
1367             },
1368             url : (this.downloadURL || (baseURL + '/GnumericToExcel/')) + name + '.xls',
1369             success : function() {
1370                 Roo.MessageBox.alert("Alert", "File should have downloaded now");
1371                 if (callback) {
1372                     callback();
1373                 }
1374             }
1375         });
1376          
1377     }
1378
1379 });