Pman.Request.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         valueType : 0,
68         valueFormat : '',
69         value : '',
70         colspan: 1,
71         rowspan: 1
72           
73     };
74      
75     this.load();
76     
77    
78     
79     
80 }
81 Roo.extend(Pman.Gnumeric, Roo.util.Observable, {
82     
83     /**
84      * @cfg {String} url the source of the Gnumeric document.
85      */
86     url : false,
87       /**
88      * @cfg {Object} data overlay data for spreadsheet - from constructor.
89      */
90     data : false,
91      /**
92      * @cfg {String} downloadUrl where GnumerictoExcel.php is...
93      */
94      
95     downloadUrl : false,
96     
97     /**
98      * @type {XmlDocument} doc the gnumeric xml document
99      */
100     doc : false,
101     
102     /**
103      * @type {XmlNode} sheet the 'Sheet' element 
104      */
105     sheet : false,
106     /**
107      * @type {Object} grid the map[row][col] = cellData 
108      */
109     grid : false,
110     /**
111      * @type {Object} colInfo - list of column sizes
112      */
113     colInfo : false,
114     /**
115      * @type {Object} rowInfo - list of row sizes
116      */
117     rowInfo : false,
118     
119     /**
120      * @type {Number} cmax - maximum number of columns
121      */
122     cmax: false,
123     /**
124      * @type {Object} rmax - maximum number of rows
125      */
126     rmax : false,
127        /**
128      * @type {String} stylesheetID id of stylesheet created to render spreadsheat
129      */
130     stylesheetID : false,
131     /**
132      * load:
133      * run the connection, parse document and fire load event..
134      * can be run multiple times with new data..
135      * 
136     */
137     
138     load : function(url)
139     {
140         this.url = url || this.url;
141         if (!this.url) {
142             return;
143         }
144         // reset stufff..
145         this.doc = false;
146         this.sheet = false;
147         this.grid = false;
148         this.colInfo = false;
149         this.rowInfo = false;
150         this.cmax = false;
151         this.rmax = false;
152         
153         if (this.stylesheetID) {
154             
155             Roo.util.CSS.removeStyleSheet(this.stylesheetID);
156             this.stylesheetID = false;
157             
158         }
159         
160         _t = this;
161         var c = new Roo.data.Connection();
162         c.request({
163             url: this.url,
164             method:  'GET',
165             success : function(resp, opts) {
166                 _t.response = resp;
167                 _t.doc = resp.responseXML;
168                 _t.parseDoc();
169                 _t.parseStyles();
170                 _t.overlayStyles();
171                 _t.applyData();
172     
173                 _t.fireEvent('load', _t);
174             },
175             failure : function()
176             {
177                 Roo.MessageBox.alert("Error", "Failed to Load Template for Spreadsheet");
178             }
179         });
180         
181
182     },
183     
184     
185      
186     
187     
188     /**
189      * toRC:
190      * convert 'A1' style position to row/column reference
191      * 
192      * @arg {String} k cell name
193      * @return {Object}  as { r: {Number} , c: {Number}  }
194      */
195     
196     toRC : function(k)
197     {
198           var c = k.charCodeAt(0)-64;
199         var n = k.substring(1);
200         if (k.charCodeAt(1) > 64) {
201             c *=26;
202             c+=k.charCodeAt(1)-64;
203             n = k.substring(2);
204         }
205         return { c:c -1 ,r: (n*1)-1 }
206     },
207       /**
208      * rangeToRC:
209      * convert 'A1:B1' style position to array of row/column references
210      * 
211      * @arg {String} k cell range
212      * @return {Array}  as [ { r: {Number} , c: {Number}  }. { r: {Number} , c: {Number}  } ]
213      */
214     rangeToRC : function(s) {
215         var ar = s.split(':');
216         return [ this.toRC(ar[0]) , this.toRC(ar[1])]
217     },
218     
219     
220     
221    
222     
223     /**
224      * parseDoc:
225      * convert XML document into cells and other data..
226      * 
227      */
228     parseDoc : function() 
229     {
230         var _t = this;
231         this.grid = {}
232         this.rmax = 1;
233         this.cmax = 1;
234         
235         this.sheet = _t.doc.getElementsByTagNameNS('*','Sheet')[0];
236         var cells = this.sheet.getElementsByTagNameNS('*','Cell');
237
238         
239         
240         Roo.each(cells, function(c) {
241            // Roo.log(c);
242             var row = c.getAttribute('Row') * 1;
243             var col = c.getAttribute('Col') * 1;
244             _t.cmax = Math.max(col+1, _t.cmax);
245             _t.rmax = Math.max(row+1, _t.rmax);
246             var vt = c.getAttribute('ValueType');
247             var vf = c.getAttribute('ValueFormat');
248             var val = c.textContent;
249             
250             if (typeof(_t.grid[row]) == 'undefined') {
251                 _t.grid[row] ={};
252             }
253             _t.grid[row][col] = Roo.applyIf({
254                 valueType : vt,
255                 valueFormat : vf,
256                 value : val,
257                 dom: c
258             }, _t.defaultCell);
259         });
260        
261         for (var r = 0; r < this.rmax;r++) {
262             if (typeof(this.grid[r]) == 'undefined') {
263               this.grid[r] ={};
264             }
265             for (var c = 0; c < this.cmax;c++) {
266                 if (typeof(this.grid[r][c]) == 'undefined') {
267                     continue;
268                 }
269                 //this.print( "[" + r + "]["+c+"]=" + grid[r][c].value +'<br/>');
270             }
271         }
272         
273         var merge = this.sheet.getElementsByTagNameNS('*','Merge');
274
275         Roo.each(merge, function(c) {
276             var rc = _t.rangeToRC(c.textContent);
277             //Roo.log(JSON.stringify(rc))
278             if (typeof(_t.grid[rc[0].r][rc[0].c]) == 'undefined') {
279                 _t.grid[rc[0].r][rc[0].c] =  Roo.apply({}, _t.defaultCell);
280             }
281                 
282             _t.grid[rc[0].r][rc[0].c].colspan = (rc[1].c - rc[0].c) + 1;
283             _t.grid[rc[0].r][rc[0].c].rowspan = (rc[1].r - rc[0].r) + 1;
284             for(var r = (rc[0].r); r < (rc[1].r+1); r++) {
285                for(var c = rc[0].c; c < (rc[1].c+1); c++) {
286                     //Roo.log('adding alias : ' + r+','+c);
287                    _t.grid[r][c] = _t.grid[rc[0].r][rc[0].c];
288                }
289            }
290             
291             
292         });
293         // read colinfo..
294         var ci = this.sheet.getElementsByTagNameNS('*','ColInfo');
295         this.colInfo = {};
296         
297         Roo.each(ci, function(c) {
298             var count = c.getAttribute('Count') || 1;
299             var s =  c.getAttribute('No')*1;
300             for(var i =0; i < count; i++) {
301                 _t.colInfo[s+i] = Math.floor(c.getAttribute('Unit')*1);
302             }
303         });
304         
305         
306         ci = this.sheet.getElementsByTagNameNS('*','RowInfo');
307         
308         this.rowInfo = {};
309         Roo.each(ci, function(c) {
310             var count = c.getAttribute('Count') || 1;
311             var s =  c.getAttribute('No')*1;
312             for(var i =0; i < count; i++) {
313                 _t.rowInfo[s+i] = Math.floor(c.getAttribute('Unit')*1);
314             }
315         });
316     
317         
318         
319      
320         
321     },
322      /**
323      * overlayStyles:
324      * put the style info onto the cell data.
325      * 
326      */
327     overlayStyles : function ()
328     {
329            // apply styles.
330         var _t = this;
331         Roo.each(this.styles, function(s) {
332        
333             for (var r = s.r; r < s.r1;r++) {
334                 if (typeof(_t.grid[r]) == 'undefined') {
335                    continue;
336                 }
337                 for (var c = s.c; c < s.c1;c++) {
338                    if (c > _t.cmax) continue;
339     
340                     if (typeof(_t.grid[r][c]) == 'undefined') _t.grid[r][c] = Roo.apply({}, _t.defaultCell);
341                     var g=_t.grid[r][c];
342                     if (typeof(g.cls) =='undefined') g.cls = [];
343                     if (g.cls.indexOf(s.name)  > -1) continue;
344                     g.cls.push(s.name);
345                 }
346             }
347         });
348     },
349      /**
350      * parseStyles: 
351      *  read the style information
352      * generates a stylesheet for the current file
353      * this should be disposed of really.....
354      * 
355      */
356     parseStyles : function() {
357                 
358         var srs = this.sheet.getElementsByTagNameNS('*','StyleRegion');
359         var _t  = this;
360         var ent = {};
361         
362         var map =  {
363             HAlign : function(ent,v) { 
364                 ent['text-align'] = { '1' : 'left', '8': 'center', '4' : 'right'}[v] || 'left';
365             },
366             VAlign : function(ent,v) { 
367                 ent['vertical-align'] = { '1' : 'top', '4': 'middel', '8' : 'bottom'}[v]  || 'top'
368             },
369             Fore : function(ent,v) { 
370                 var col=[];
371                 Roo.each(v.split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
372                 ent['color'] = 'rgb(' + col.join(',') + ')';
373             },
374             Back : function(ent,v) { 
375                 var col=[];
376                 Roo.each(v.split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
377                 ent['background-color'] = 'rgb(' + col.join(',') + ')';
378             },
379             FontUnit : function(ent,v) { 
380                 ent['font-size'] = v + 'px';
381             },
382             FontBold : function(ent,v) { 
383                 if (v*1 < 1) return;
384                 ent['font-weight'] = 'bold';
385             },
386             FontItalic : function(ent,v) { 
387                 if (v*0 < 1) return;
388                 //ent['font-weight'] = 'bold';
389             },
390             FontName : function(ent,v) { 
391                 ent['font-family'] = v;
392             },
393             BorderStyle : function(ent,v) { 
394                 var vv  = v.split('-');
395                 ent['border-'+vv[0]+'-style'] = 'solid';
396                 ent['border-'+vv[0]+'-width'] = vv[1]+'px';
397             },
398             BorderColor : function(ent,v) { 
399                 var vv  = v.split('-');
400                 var col=[];
401                 Roo.each(vv[1].split(':'), function(c) { col.push(Math.round(parseInt(c,16)/256)); })
402                 ent['border-'+vv[0]+'-color'] = 'rgb(' + col.join(',') + ')';
403             }
404         }
405         function add(e, k, v) {
406             //Roo.log(k,v);
407             e.gstyle[k] = v;
408             if (typeof(map[k]) == 'undefined') {
409                 return;
410             }
411             map[k](e.style,v);    
412         }
413         var css = {};
414         var styles = [];
415         var sid= Roo.id();
416         
417         
418         Roo.each(srs, function(sr,n)
419         {
420             ent = {
421                 c : sr.getAttribute('startCol') *1,
422                 r : sr.getAttribute('startRow')*1,
423                 c1 : (sr.getAttribute('endCol')*1) +1,
424                 r1 : (sr.getAttribute('endRow')*1) +1,
425                 style : {},
426                 gstyle : {},
427                 name : sid +'-gstyle-' + n
428                 
429             };
430     
431             Roo.each(sr.getElementsByTagNameNS('*','Style')[0].attributes, function(e) { 
432                 add(ent, e.name, e.value);
433             });
434             if (sr.getElementsByTagNameNS('*','Font').length) {
435                 Roo.each(sr.getElementsByTagNameNS('*','Font')[0].attributes, function(e) { 
436                      add(ent, 'Font'+e.name, e.value);
437     
438                 });
439                 add(ent, 'FontName', sr.getElementsByTagNameNS('*','Font')[0].textContent);
440     
441             }
442             if (sr.getElementsByTagNameNS('*','StyleBorder').length) {
443                 Roo.each(sr.getElementsByTagNameNS('*','StyleBorder')[0].childNodes, function(e) {
444                     if (!e.tagName) {
445                         return;
446                     }
447                     Roo.each(e.attributes, function(ea) { 
448                         add(ent, 'Border'+ea.name, e.tagName.split(':')[1].toLowerCase() + '-' + ea.value);
449                     });
450                 })
451                     
452             }
453             styles.push(ent);
454             css['.'+ent.name] = ent.style;
455         });
456         
457         this.styles = styles;
458         
459         this.stylesheetID = sid;
460         Roo.util.CSS.createStyleSheet(css, sid);
461     },
462
463     /* ---------------------------------------  AFTER LOAD METHODS... ----------------------- */
464     /**
465      * set: 
466      * Set the value of a cell..
467      * @param {String} cell name of cell, eg. C10
468      * @param {Value} value to put in cell..
469      * 
470      * Cells should exist at present, we do not make them up...
471      */
472      
473     
474     set : function(cell, v) {
475         
476         var cs= typeof(cell) == 'string' ? this.toRC(cell) : cell;
477         Roo.log(    this.grid[cs.r][cs.c]);
478         this.grid[cs.r][cs.c].value=  v;
479         // need to generate clell if it doe
480         if (typeof(this.grid[cs.r][cs.c].dom) == 'undefined') {
481             Roo.log('no default content for cell:' + cell);
482             return;
483         }
484         this.grid[cs.r][cs.c].dom.textContent=  v;
485     },
486     /**
487      * applyData: 
488      * Set the value of a cell..
489      * @param {String} cell name of cell, eg. C10
490      * @param {Value} value to put in cell..
491      * 
492      * Cells should exist at present, we do not make them up...
493      */
494      
495     applyData : function(data)
496     {
497         
498         data = data || this.data;
499         for (var r = 0; r < this.rmax;r++) {
500             if (typeof(this.grid[r]) == 'undefined') continue;
501             for (var c = 0; c < this.cmax;c++) {  
502                 if (typeof(this.grid[r][c]) == 'undefined') {
503                     continue;
504                 }
505                 if (!this.grid[r][c].value.length 
506                         || !this.grid[r][c].value.match(/\{/)) {
507                     continue;
508                 }
509                 
510                 var x = new Roo.Template({ html: this.grid[r][c].value });
511                 try {
512                     var res = x.applyTemplate(data);
513                     //Roo.log("set " + r  + "," + c + ":"+res)
514                     this.set({ r: r, c: c}, x.applyTemplate(data));
515                 } catch (e) {
516                  //   Roo.log(e.toString());
517                   //  Roo.log(e);
518                     // continue?
519                 }
520                 
521             }
522         }
523             
524     },
525     
526     
527      /**
528      * toHTML: 
529      * Convert spreadsheet into a HTML table.
530      */
531             
532     toHTML :function()
533     {
534          var _t = this;
535         function calcWidth(sc, span)
536         {
537             var n =0;
538             for(var i =sc; i< sc+span;i++) {
539                 n+=_t.colInfo[i];
540             }   
541             return n;
542         }
543         
544         var grid = this.grid;
545         // lets do a basic dump..
546         var out = '<table style="table-layout:fixed;" cellpadding="0" cellspacing="0">';
547         for (var r = 0; r < this.rmax;r++) {
548             out += '<tr style="height:'+this.rowInfo[r]+'px;">';
549             for (var c = 0; c < this.cmax;c++) {
550                 var g = (typeof(grid[r][c]) == 'undefined') ? defaultCell  : grid[r][c];
551                 if (typeof(g.cls) =='undefined') g.cls = [];
552                 var w= calcWidth(c,g.colspan);
553                 out+=String.format('<td colspan="{0}" rowspan="{1}"  class="{4}"><div style="{3}">{2}</div></td>', 
554                     g.colspan, g.rowspan, g.value,
555                     'overflow:hidden;' + 
556                     'width:'+w+'px;' +
557                    
558                     'text-overflow:ellipsis;' +
559                     'white-space:nowrap;',
560                      g.cls.join(' ')
561     
562     
563                 );
564                 c+=(g.colspan-1);
565             }
566             out += '</tr>';
567         }
568         //Roo.log(out);
569         return out+'</table>';
570         
571         
572         
573     },
574     /**
575      * download:
576      * @param {String} name  filename to downlaod (without xls)
577      * @param {String} callback  (optional) - callback to call after callback is complete.
578      */
579     download : function(name,callback)
580     {
581         name = name || "Missing_download_filename";
582         
583         var ser = new XMLSerializer();
584         var x = new Pman.Download({
585             method: 'POST',
586             params : {
587                xml : ser.serializeToString(this.doc),
588                format : 'xls', //xml
589                debug : 0
590                
591             },
592             url : (this.downloadUrl || (baseURL + '/GnumericToExcel/')) + name + '.xls',
593             success : function() {
594                 Roo.MessageBox.alert("Alert", "File should have downloaded now");
595                 if (callback) {
596                     callback();
597                 }
598             }
599         });
600          
601     }
602
603 });