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