├── Base64.js
├── Button.js
├── CSVFormatter
└── CSVFormatter.js
├── ExcelFormatter
├── Cell.js
├── ExcelFormatter.js
├── Style.js
├── Workbook.js
└── Worksheet.js
├── Exporter-all.js
├── Exporter.js
├── Formatter.js
├── LICENSE.txt
├── README.textile
├── build
└── example
├── index.html
└── style.css
/Base64.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Base64 encode / decode
4 | * http://www.webtoolkit.info/
5 | *
6 | **/
7 |
8 | var Base64 = (function() {
9 |
10 | // private property
11 | var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
12 |
13 | // private method for UTF-8 encoding
14 | function utf8Encode(string) {
15 | string = string.replace(/\r\n/g,"\n");
16 | var utftext = "";
17 | for (var n = 0; n < string.length; n++) {
18 | var c = string.charCodeAt(n);
19 | if (c < 128) {
20 | utftext += String.fromCharCode(c);
21 | }
22 | else if((c > 127) && (c < 2048)) {
23 | utftext += String.fromCharCode((c >> 6) | 192);
24 | utftext += String.fromCharCode((c & 63) | 128);
25 | }
26 | else {
27 | utftext += String.fromCharCode((c >> 12) | 224);
28 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
29 | utftext += String.fromCharCode((c & 63) | 128);
30 | }
31 | }
32 | return utftext;
33 | }
34 |
35 | // public method for encoding
36 | return {
37 | //This was the original line, which tries to use Firefox's built in Base64 encoder, but this kept throwing exceptions....
38 | // encode : (typeof btoa == 'function') ? function(input) { return btoa(input); } : function (input) {
39 |
40 |
41 | encode : function (input) {
42 | var output = "";
43 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
44 | var i = 0;
45 | input = utf8Encode(input);
46 | while (i < input.length) {
47 | chr1 = input.charCodeAt(i++);
48 | chr2 = input.charCodeAt(i++);
49 | chr3 = input.charCodeAt(i++);
50 | enc1 = chr1 >> 2;
51 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
52 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
53 | enc4 = chr3 & 63;
54 | if (isNaN(chr2)) {
55 | enc3 = enc4 = 64;
56 | } else if (isNaN(chr3)) {
57 | enc4 = 64;
58 | }
59 | output = output +
60 | keyStr.charAt(enc1) + keyStr.charAt(enc2) +
61 | keyStr.charAt(enc3) + keyStr.charAt(enc4);
62 | }
63 | return output;
64 | }
65 | };
66 | })();
--------------------------------------------------------------------------------
/Button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.Button
3 | * @extends Ext.Button
4 | * @author Nige White, with modifications from Ed Spencer
5 | * Specialised Button class that allows downloading of data via data: urls.
6 | * Internally, this is just a link.
7 | * Pass it either an Ext.Component subclass with a 'store' property, or just a store:
8 | * new Ext.ux.Exporter.Button({component: someGrid});
9 | * new Ext.ux.Exporter.Button({store: someStore});
10 | * @cfg {Ext.Component} component The component the store is bound to
11 | * @cfg {Ext.data.Store} store The store to export (alternatively, pass a component with a store property)
12 | */
13 | Ext.ux.Exporter.Button = Ext.extend(Ext.Button, {
14 | constructor: function(config) {
15 | config = config || {};
16 |
17 | Ext.applyIf(config, {
18 | exportFunction: 'exportGrid',
19 | disabled : true,
20 | text : 'Download',
21 | cls : 'download'
22 | });
23 |
24 | if (config.store == undefined && config.component != undefined) {
25 | Ext.applyIf(config, {
26 | store: config.component.store
27 | });
28 | } else {
29 | Ext.applyIf(config, {
30 | component: {
31 | store: config.store
32 | }
33 | });
34 | }
35 |
36 | Ext.ux.Exporter.Button.superclass.constructor.call(this, config);
37 |
38 | if (this.store && Ext.isFunction(this.store.on)) {
39 | var setLink = function() {
40 | this.getEl().child('a', true).href = 'data:application/vnd.ms-excel;base64,' + Ext.ux.Exporter[config.exportFunction](this.component, null, config);
41 |
42 | this.enable();
43 | };
44 |
45 | if (this.el) {
46 | setLink.call(this);
47 | } else {
48 | this.on('render', setLink, this);
49 | }
50 |
51 | this.store.on('load', setLink, this);
52 | }
53 | },
54 |
55 | template: new Ext.Template(
56 | '
"),
59 |
60 | onRender: function(ct, position){
61 | var btn, targs = [this.text || ' ', this.href, this.target || "_self"];
62 | if (position){
63 | btn = this.template.insertBefore(position, targs, true);
64 | }else{
65 | btn = this.template.append(ct, targs, true);
66 | }
67 | var btnEl = btn.child("a:first");
68 | this.btnEl = btnEl;
69 | btnEl.on('focus', this.onFocus, this);
70 | btnEl.on('blur', this.onBlur, this);
71 |
72 | this.initButtonEl(btn, btnEl);
73 | Ext.ButtonToggleMgr.register(this);
74 | },
75 |
76 | onClick : function(e){
77 | if (e.button != 0) return;
78 |
79 | if (!this.disabled){
80 | this.fireEvent("click", this, e);
81 |
82 | if (this.handler) this.handler.call(this.scope || this, this, e);
83 | }
84 | }
85 | });
86 |
87 | Ext.reg('exportbutton', Ext.ux.Exporter.Button);
--------------------------------------------------------------------------------
/CSVFormatter/CSVFormatter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.CSVFormatter
3 | * @extends Ext.ux.Exporter.Formatter
4 | * Specialised Format class for outputting .csv files
5 | */
6 | Ext.ux.Exporter.CSVFormatter = Ext.extend(Ext.ux.Exporter.Formatter, {
7 |
8 | });
--------------------------------------------------------------------------------
/ExcelFormatter/Cell.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.ExcelFormatter.Cell
3 | * @extends Object
4 | * Represents a single cell in a worksheet
5 | */
6 | Ext.ux.Exporter.ExcelFormatter.Cell = Ext.extend(Object, {
7 |
8 | constructor: function(config) {
9 | Ext.applyIf(config, {
10 | type: "String"
11 | });
12 |
13 | Ext.apply(this, config);
14 |
15 | Ext.ux.Exporter.ExcelFormatter.Cell.superclass.constructor.apply(this, arguments);
16 | },
17 |
18 | render: function() {
19 | return this.tpl.apply(this);
20 | },
21 |
22 | tpl: new Ext.XTemplate(
23 | '',
24 | '',
25 | ''
26 | )
27 | });
--------------------------------------------------------------------------------
/ExcelFormatter/ExcelFormatter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.ExcelFormatter
3 | * @extends Ext.ux.Exporter.Formatter
4 | * Specialised Format class for outputting .xls files
5 | */
6 | Ext.ux.Exporter.ExcelFormatter = Ext.extend(Ext.ux.Exporter.Formatter, {
7 |
8 | format: function(store, config) {
9 | var workbook = new Ext.ux.Exporter.ExcelFormatter.Workbook(config);
10 | workbook.addWorksheet(store, config || {});
11 |
12 | return workbook.render();
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/ExcelFormatter/Style.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.ExcelFormatter.Style
3 | * @extends Object
4 | * Represents a style declaration for a Workbook (this is like defining CSS rules). Example:
5 | *
6 | * new Ext.ux.Exporter.ExcelFormatter.Style({
7 | * attributes: [
8 | * {
9 | * name: "Alignment",
10 | * properties: [
11 | * {name: "Vertical", value: "Top"},
12 | * {name: "WrapText", value: "1"}
13 | * ]
14 | * },
15 | * {
16 | * name: "Borders",
17 | * children: [
18 | * name: "Border",
19 | * properties: [
20 | * {name: "Color", value: "#e4e4e4"},
21 | * {name: "Weight", value: "1"}
22 | * ]
23 | * ]
24 | * }
25 | * ]
26 | * })
27 | *
28 | * @cfg {String} id The ID of this style (required)
29 | * @cfg {Array} attributes The attributes for this style
30 | * @cfg {String} parentStyle The (optional parentStyle ID)
31 | */
32 | Ext.ux.Exporter.ExcelFormatter.Style = Ext.extend(Object, {
33 |
34 | constructor: function(config) {
35 | config = config || {};
36 |
37 | Ext.apply(this, config, {
38 | parentStyle: '',
39 | attributes : []
40 | });
41 |
42 | Ext.ux.Exporter.ExcelFormatter.Style.superclass.constructor.apply(this, arguments);
43 |
44 | if (this.id == undefined) throw new Error("An ID must be provided to Style");
45 |
46 | this.preparePropertyStrings();
47 | },
48 |
49 | /**
50 | * Iterates over the attributes in this style, and any children they may have, creating property
51 | * strings on each suitable for use in the XTemplate
52 | */
53 | preparePropertyStrings: function() {
54 | Ext.each(this.attributes, function(attr, index) {
55 | this.attributes[index].propertiesString = this.buildPropertyString(attr);
56 | this.attributes[index].children = attr.children || [];
57 |
58 | Ext.each(attr.children, function(child, childIndex) {
59 | this.attributes[index].children[childIndex].propertiesString = this.buildPropertyString(child);
60 | }, this);
61 | }, this);
62 | },
63 |
64 | /**
65 | * Builds a concatenated property string for a given attribute, suitable for use in the XTemplate
66 | */
67 | buildPropertyString: function(attribute) {
68 | var propertiesString = "";
69 |
70 | Ext.each(attribute.properties || [], function(property) {
71 | propertiesString += String.format('ss:{0}="{1}" ', property.name, property.value);
72 | }, this);
73 |
74 | return propertiesString;
75 | },
76 |
77 | render: function() {
78 | return this.tpl.apply(this);
79 | },
80 |
81 | tpl: new Ext.XTemplate(
82 | '',
83 | '',
84 | '',
85 | '',
86 | '',
87 | '',
88 | '',
89 | '',
90 | '',
91 | '',
92 | '',
93 | '',
94 | '',
95 | '',
96 | '',
97 | '',
98 | '',
99 | '',
100 | ''
101 | )
102 | });
--------------------------------------------------------------------------------
/ExcelFormatter/Workbook.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.ExcelFormatter.Workbook
3 | * @extends Object
4 | * Represents an Excel workbook
5 | */
6 | Ext.ux.Exporter.ExcelFormatter.Workbook = Ext.extend(Object, {
7 |
8 | constructor: function(config) {
9 | config = config || {};
10 |
11 | Ext.apply(this, config, {
12 | /**
13 | * @property title
14 | * @type String
15 | * The title of the workbook (defaults to "Workbook")
16 | */
17 | title: "Workbook",
18 |
19 | /**
20 | * @property worksheets
21 | * @type Array
22 | * The array of worksheets inside this workbook
23 | */
24 | worksheets: [],
25 |
26 | /**
27 | * @property compileWorksheets
28 | * @type Array
29 | * Array of all rendered Worksheets
30 | */
31 | compiledWorksheets: [],
32 |
33 | /**
34 | * @property cellBorderColor
35 | * @type String
36 | * The colour of border to use for each Cell
37 | */
38 | cellBorderColor: "#e4e4e4",
39 |
40 | /**
41 | * @property styles
42 | * @type Array
43 | * The array of Ext.ux.Exporter.ExcelFormatter.Style objects attached to this workbook
44 | */
45 | styles: [],
46 |
47 | /**
48 | * @property compiledStyles
49 | * @type Array
50 | * Array of all rendered Ext.ux.Exporter.ExcelFormatter.Style objects for this workbook
51 | */
52 | compiledStyles: [],
53 |
54 | /**
55 | * @property hasDefaultStyle
56 | * @type Boolean
57 | * True to add the default styling options to all cells (defaults to true)
58 | */
59 | hasDefaultStyle: true,
60 |
61 | /**
62 | * @property hasStripeStyles
63 | * @type Boolean
64 | * True to add the striping styles (defaults to true)
65 | */
66 | hasStripeStyles: true,
67 |
68 | windowHeight : 9000,
69 | windowWidth : 50000,
70 | protectStructure: false,
71 | protectWindows : false
72 | });
73 |
74 | if (this.hasDefaultStyle) this.addDefaultStyle();
75 | if (this.hasStripeStyles) this.addStripedStyles();
76 |
77 | this.addTitleStyle();
78 | this.addHeaderStyle();
79 | },
80 |
81 | render: function() {
82 | this.compileStyles();
83 | this.joinedCompiledStyles = this.compiledStyles.join("");
84 |
85 | this.compileWorksheets();
86 | this.joinedWorksheets = this.compiledWorksheets.join("");
87 |
88 | return this.tpl.apply(this);
89 | },
90 |
91 | /**
92 | * Adds a worksheet to this workbook based on a store and optional config
93 | * @param {Ext.data.Store} store The store to initialize the worksheet with
94 | * @param {Object} config Optional config object
95 | * @return {Ext.ux.Exporter.ExcelFormatter.Worksheet} The worksheet
96 | */
97 | addWorksheet: function(store, config) {
98 | var worksheet = new Ext.ux.Exporter.ExcelFormatter.Worksheet(store, config);
99 |
100 | this.worksheets.push(worksheet);
101 |
102 | return worksheet;
103 | },
104 |
105 | /**
106 | * Adds a new Ext.ux.Exporter.ExcelFormatter.Style to this Workbook
107 | * @param {Object} config The style config, passed to the Style constructor (required)
108 | */
109 | addStyle: function(config) {
110 | var style = new Ext.ux.Exporter.ExcelFormatter.Style(config || {});
111 |
112 | this.styles.push(style);
113 |
114 | return style;
115 | },
116 |
117 | /**
118 | * Compiles each Style attached to this Workbook by rendering it
119 | * @return {Array} The compiled styles array
120 | */
121 | compileStyles: function() {
122 | this.compiledStyles = [];
123 |
124 | Ext.each(this.styles, function(style) {
125 | this.compiledStyles.push(style.render());
126 | }, this);
127 |
128 | return this.compiledStyles;
129 | },
130 |
131 | /**
132 | * Compiles each Worksheet attached to this Workbook by rendering it
133 | * @return {Array} The compiled worksheets array
134 | */
135 | compileWorksheets: function() {
136 | this.compiledWorksheets = [];
137 |
138 | Ext.each(this.worksheets, function(worksheet) {
139 | this.compiledWorksheets.push(worksheet.render());
140 | }, this);
141 |
142 | return this.compiledWorksheets;
143 | },
144 |
145 | tpl: new Ext.XTemplate(
146 | '',
147 | '',
148 | '',
149 | '{title}',
150 | '',
151 | '',
152 | '{windowHeight}',
153 | '{windowWidth}',
154 | '{protectStructure}',
155 | '{protectWindows}',
156 | '',
157 | '',
158 | '{joinedCompiledStyles}',
159 | '',
160 | '{joinedWorksheets}',
161 | ''
162 | ),
163 |
164 | /**
165 | * Adds the default Style to this workbook. This sets the default font face and size, as well as cell borders
166 | */
167 | addDefaultStyle: function() {
168 | var borderProperties = [
169 | {name: "Color", value: this.cellBorderColor},
170 | {name: "Weight", value: "1"},
171 | {name: "LineStyle", value: "Continuous"}
172 | ];
173 |
174 | this.addStyle({
175 | id: 'Default',
176 | attributes: [
177 | {
178 | name: "Alignment",
179 | properties: [
180 | {name: "Vertical", value: "Top"},
181 | {name: "WrapText", value: "1"}
182 | ]
183 | },
184 | {
185 | name: "Font",
186 | properties: [
187 | {name: "FontName", value: "arial"},
188 | {name: "Size", value: "10"}
189 | ]
190 | },
191 | {name: "Interior"}, {name: "NumberFormat"}, {name: "Protection"},
192 | {
193 | name: "Borders",
194 | children: [
195 | {
196 | name: "Border",
197 | properties: [{name: "Position", value: "Top"}].concat(borderProperties)
198 | },
199 | {
200 | name: "Border",
201 | properties: [{name: "Position", value: "Bottom"}].concat(borderProperties)
202 | },
203 | {
204 | name: "Border",
205 | properties: [{name: "Position", value: "Left"}].concat(borderProperties)
206 | },
207 | {
208 | name: "Border",
209 | properties: [{name: "Position", value: "Right"}].concat(borderProperties)
210 | }
211 | ]
212 | }
213 | ]
214 | });
215 | },
216 |
217 | addTitleStyle: function() {
218 | this.addStyle({
219 | id: "title",
220 | attributes: [
221 | {name: "Borders"},
222 | {name: "Font"},
223 | {
224 | name: "NumberFormat",
225 | properties: [
226 | {name: "Format", value: "@"}
227 | ]
228 | },
229 | {
230 | name: "Alignment",
231 | properties: [
232 | {name: "WrapText", value: "1"},
233 | {name: "Horizontal", value: "Center"},
234 | {name: "Vertical", value: "Center"}
235 | ]
236 | }
237 | ]
238 | });
239 | },
240 |
241 | addHeaderStyle: function() {
242 | this.addStyle({
243 | id: "headercell",
244 | attributes: [
245 | {
246 | name: "Font",
247 | properties: [
248 | {name: "Bold", value: "1"},
249 | {name: "Size", value: "10"}
250 | ]
251 | },
252 | {
253 | name: "Interior",
254 | properties: [
255 | {name: "Pattern", value: "Solid"},
256 | {name: "Color", value: "#A3C9F1"}
257 | ]
258 | },
259 | {
260 | name: "Alignment",
261 | properties: [
262 | {name: "WrapText", value: "1"},
263 | {name: "Horizontal", value: "Center"}
264 | ]
265 | }
266 | ]
267 | });
268 | },
269 |
270 | /**
271 | * Adds the default striping styles to this workbook
272 | */
273 | addStripedStyles: function() {
274 | this.addStyle({
275 | id: "even",
276 | attributes: [
277 | {
278 | name: "Interior",
279 | properties: [
280 | {name: "Pattern", value: "Solid"},
281 | {name: "Color", value: "#CCFFFF"}
282 | ]
283 | }
284 | ]
285 | });
286 |
287 | this.addStyle({
288 | id: "odd",
289 | attributes: [
290 | {
291 | name: "Interior",
292 | properties: [
293 | {name: "Pattern", value: "Solid"},
294 | {name: "Color", value: "#CCCCFF"}
295 | ]
296 | }
297 | ]
298 | });
299 |
300 | Ext.each(['even', 'odd'], function(parentStyle) {
301 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'date', "[ENG][$-409]dd\-mmm\-yyyy;@");
302 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'int', "0");
303 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'float', "0.00");
304 | }, this);
305 | },
306 |
307 | /**
308 | * Private convenience function to easily add a NumberFormat style for a given parentStyle
309 | * @param {String} parentStyle The ID of the parentStyle Style
310 | * @param {String} id The ID of the new style
311 | * @param {String} value The value of the NumberFormat's Format property
312 | */
313 | addChildNumberFormatStyle: function(parentStyle, id, value) {
314 | this.addStyle({
315 | id: id,
316 | parentStyle: "even",
317 | attributes: [
318 | {
319 | name: "NumberFormat",
320 | properties: [{name: "Format", value: value}]
321 | }
322 | ]
323 | });
324 | }
325 | });
--------------------------------------------------------------------------------
/ExcelFormatter/Worksheet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.ExcelFormatter.Worksheet
3 | * @extends Object
4 | * Represents an Excel worksheet
5 | * @cfg {Ext.data.Store} store The store to use (required)
6 | */
7 | Ext.ux.Exporter.ExcelFormatter.Worksheet = Ext.extend(Object, {
8 |
9 | constructor: function(store, config) {
10 | config = config || {};
11 |
12 | this.store = store;
13 |
14 | Ext.applyIf(config, {
15 | hasTitle : true,
16 | hasHeadings: true,
17 | stripeRows : true,
18 |
19 | title : "Workbook",
20 | columns : store.fields == undefined ? {} : store.fields.items
21 | });
22 |
23 | Ext.apply(this, config);
24 |
25 | Ext.ux.Exporter.ExcelFormatter.Worksheet.superclass.constructor.apply(this, arguments);
26 | },
27 |
28 | /**
29 | * @property dateFormatString
30 | * @type String
31 | * String used to format dates (defaults to "Y-m-d"). All other data types are left unmolested
32 | */
33 | dateFormatString: "Y-m-d",
34 |
35 | worksheetTpl: new Ext.XTemplate(
36 | '',
37 | '',
38 | '',
39 | '',
40 | '',
41 | '{columns}',
42 | '',
43 | '',
44 | '',
45 | '{title}',
46 | '',
47 | '',
48 | '',
49 | '',
50 | '{header}',
51 | '',
52 | '{rows}',
53 | '',
54 | '',
55 | '',
56 | '',
57 | '',
58 | '',
59 | '',
60 | '',
61 | '',
62 | 'Blank',
63 | '1',
64 | '32767',
65 | '',
66 | '600',
67 | '',
68 | '',
69 | '',
70 | 'False',
71 | 'False',
72 | '',
73 | ''
74 | ),
75 |
76 | /**
77 | * Builds the Worksheet XML
78 | * @param {Ext.data.Store} store The store to build from
79 | */
80 | render: function(store) {
81 | return this.worksheetTpl.apply({
82 | header : this.buildHeader(),
83 | columns : this.buildColumns().join(""),
84 | rows : this.buildRows().join(""),
85 | colCount: this.columns.length,
86 | rowCount: this.store.getCount() + 2,
87 | title : this.title
88 | });
89 | },
90 |
91 | buildColumns: function() {
92 | var cols = [];
93 |
94 | Ext.each(this.columns, function(column) {
95 | cols.push(this.buildColumn());
96 | }, this);
97 |
98 | return cols;
99 | },
100 |
101 | buildColumn: function(width) {
102 | return String.format('', width || 164);
103 | },
104 |
105 | buildRows: function() {
106 | var rows = [];
107 |
108 | this.store.each(function(record, index) {
109 | rows.push(this.buildRow(record, index));
110 | }, this);
111 |
112 | return rows;
113 | },
114 |
115 | buildHeader: function() {
116 | var cells = [];
117 |
118 | Ext.each(this.columns, function(col) {
119 | var title;
120 |
121 | if (col.header != undefined) {
122 | title = col.header;
123 | } else {
124 | //make columns taken from Record fields (e.g. with a col.name) human-readable
125 | title = col.name.replace(/_/g, " ");
126 | title = title.charAt(0).toUpperCase() + title.substr(1).toLowerCase();
127 | }
128 |
129 | cells.push(String.format('{0}', title));
130 | }, this);
131 |
132 | return cells.join("");
133 | },
134 |
135 | buildRow: function(record, index) {
136 | var style,
137 | cells = [];
138 | if (this.stripeRows === true) style = index % 2 == 0 ? 'even' : 'odd';
139 |
140 | Ext.each(this.columns, function(col) {
141 | var name = col.name || col.dataIndex;
142 |
143 | //if given a renderer via a ColumnModel, use it and ensure data type is set to String
144 | if (Ext.isFunction(col.renderer)) {
145 | var value = col.renderer(record.get(name), null, record),
146 | type = "String";
147 | } else {
148 | var value = record.get(name),
149 | type = this.typeMappings[col.type || record.fields.item(name).type];
150 | }
151 |
152 | cells.push(this.buildCell(value, type, style).render());
153 | }, this);
154 |
155 | return String.format("{0}", cells.join(""));
156 | },
157 |
158 | buildCell: function(value, type, style) {
159 | if (type == "DateTime" && Ext.isFunction(value.format)) value = value.format(this.dateFormatString);
160 |
161 | return new Ext.ux.Exporter.ExcelFormatter.Cell({
162 | value: value,
163 | type : type,
164 | style: style
165 | });
166 | },
167 |
168 | /**
169 | * @property typeMappings
170 | * @type Object
171 | * Mappings from Ext.data.Record types to Excel types
172 | */
173 | typeMappings: {
174 | 'int' : "Number",
175 | 'string': "String",
176 | 'float' : "Number",
177 | 'date' : "DateTime"
178 | }
179 | });
--------------------------------------------------------------------------------
/Exporter-all.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Base64 encode / decode
4 | * http://www.webtoolkit.info/
5 | *
6 | **/
7 |
8 | var Base64 = (function() {
9 |
10 | // private property
11 | var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
12 |
13 | // private method for UTF-8 encoding
14 | function utf8Encode(string) {
15 | string = string.replace(/\r\n/g,"\n");
16 | var utftext = "";
17 | for (var n = 0; n < string.length; n++) {
18 | var c = string.charCodeAt(n);
19 | if (c < 128) {
20 | utftext += String.fromCharCode(c);
21 | }
22 | else if((c > 127) && (c < 2048)) {
23 | utftext += String.fromCharCode((c >> 6) | 192);
24 | utftext += String.fromCharCode((c & 63) | 128);
25 | }
26 | else {
27 | utftext += String.fromCharCode((c >> 12) | 224);
28 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
29 | utftext += String.fromCharCode((c & 63) | 128);
30 | }
31 | }
32 | return utftext;
33 | }
34 |
35 | // public method for encoding
36 | return {
37 | //This was the original line, which tries to use Firefox's built in Base64 encoder, but this kept throwing exceptions....
38 | // encode : (typeof btoa == 'function') ? function(input) { return btoa(input); } : function (input) {
39 |
40 |
41 | encode : function (input) {
42 | var output = "";
43 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
44 | var i = 0;
45 | input = utf8Encode(input);
46 | while (i < input.length) {
47 | chr1 = input.charCodeAt(i++);
48 | chr2 = input.charCodeAt(i++);
49 | chr3 = input.charCodeAt(i++);
50 | enc1 = chr1 >> 2;
51 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
52 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
53 | enc4 = chr3 & 63;
54 | if (isNaN(chr2)) {
55 | enc3 = enc4 = 64;
56 | } else if (isNaN(chr3)) {
57 | enc4 = 64;
58 | }
59 | output = output +
60 | keyStr.charAt(enc1) + keyStr.charAt(enc2) +
61 | keyStr.charAt(enc3) + keyStr.charAt(enc4);
62 | }
63 | return output;
64 | }
65 | };
66 | })();
67 |
68 | /**
69 | * @class Ext.ux.Exporter
70 | * @author Ed Spencer (http://edspencer.net)
71 | * Class providing a common way of downloading data in .xls or .csv format
72 | */
73 | Ext.ux.Exporter = function() {
74 | return {
75 | /**
76 | * Exports a grid, using the .xls formatter by default
77 | * @param {Ext.grid.GridPanel} grid The grid to export from
78 | * @param {Object} config Optional config settings for the formatter
79 | */
80 | exportGrid: function(grid, formatter, config) {
81 | config = config || {};
82 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
83 |
84 | Ext.applyIf(config, {
85 | title : grid.title,
86 | columns: grid.getColumnModel().config
87 | });
88 |
89 | return Base64.encode(formatter.format(grid.store, config));
90 | },
91 |
92 | exportStore: function(store, formatter, config) {
93 | config = config || {};
94 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
95 |
96 | Ext.applyIf(config, {
97 | columns: config.store.fields.items
98 | });
99 |
100 | return Base64.encode(formatter.format(store, config));
101 | },
102 |
103 | exportTree: function(tree, formatter, config) {
104 | config = config || {};
105 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
106 |
107 | var store = tree.store || config.store;
108 |
109 | Ext.applyIf(config, {
110 | title: tree.title
111 | });
112 |
113 | return Base64.encode(formatter.format(store, config));
114 | }
115 | };
116 | }();
117 |
118 | /**
119 | * @class Ext.ux.Exporter.Button
120 | * @extends Ext.Button
121 | * @author Nige White, with modifications from Ed Spencer
122 | * Specialised Button class that allows downloading of data via data: urls.
123 | * Internally, this is just a link.
124 | * Pass it either an Ext.Component subclass with a 'store' property, or just a store:
125 | * new Ext.ux.Exporter.Button({component: someGrid});
126 | * new Ext.ux.Exporter.Button({store: someStore});
127 | * @cfg {Ext.Component} component The component the store is bound to
128 | * @cfg {Ext.data.Store} store The store to export (alternatively, pass a component with a store property)
129 | */
130 | Ext.ux.Exporter.Button = Ext.extend(Ext.Button, {
131 | constructor: function(config) {
132 | config = config || {};
133 |
134 | Ext.applyIf(config, {
135 | exportFunction: 'exportGrid',
136 | disabled : true,
137 | text : 'Download',
138 | cls : 'download'
139 | });
140 |
141 | if (config.store == undefined && config.component != undefined) {
142 | Ext.applyIf(config, {
143 | store: config.component.store
144 | });
145 | } else {
146 | Ext.applyIf(config, {
147 | component: {
148 | store: config.store
149 | }
150 | });
151 | }
152 |
153 | Ext.ux.Exporter.Button.superclass.constructor.call(this, config);
154 |
155 | if (this.store && Ext.isFunction(this.store.on)) {
156 | var setLink = function() {
157 | this.getEl().child('a', true).href = 'data:application/vnd.ms-excel;base64,' + Ext.ux.Exporter[config.exportFunction](this.component, null, config);
158 |
159 | this.enable();
160 | };
161 |
162 | if (this.el) {
163 | setLink.call(this);
164 | } else {
165 | this.on('render', setLink, this);
166 | }
167 |
168 | this.store.on('load', setLink, this);
169 | }
170 | },
171 |
172 | template: new Ext.Template(
173 | '',
174 | ' | {0} | | ',
175 | "
"),
176 |
177 | onRender: function(ct, position){
178 | var btn, targs = [this.text || ' ', this.href, this.target || "_self"];
179 | if (position){
180 | btn = this.template.insertBefore(position, targs, true);
181 | }else{
182 | btn = this.template.append(ct, targs, true);
183 | }
184 | var btnEl = btn.child("a:first");
185 | this.btnEl = btnEl;
186 | btnEl.on('focus', this.onFocus, this);
187 | btnEl.on('blur', this.onBlur, this);
188 |
189 | this.initButtonEl(btn, btnEl);
190 | Ext.ButtonToggleMgr.register(this);
191 | },
192 |
193 | onClick : function(e){
194 | if (e.button != 0) return;
195 |
196 | if (!this.disabled){
197 | this.fireEvent("click", this, e);
198 |
199 | if (this.handler) this.handler.call(this.scope || this, this, e);
200 | }
201 | }
202 | });
203 |
204 | Ext.reg('exportbutton', Ext.ux.Exporter.Button);
205 |
206 | /**
207 | * @class Ext.ux.Exporter.Formatter
208 | * @author Ed Spencer (http://edspencer.net)
209 | * @cfg {Ext.data.Store} store The store to export
210 | */
211 | Ext.ux.Exporter.Formatter = function(config) {
212 | config = config || {};
213 |
214 | Ext.applyIf(config, {
215 |
216 | });
217 | };
218 |
219 | Ext.ux.Exporter.Formatter.prototype = {
220 | /**
221 | * Performs the actual formatting. This must be overridden by a subclass
222 | */
223 | format: Ext.emptyFn
224 | };
225 |
226 | /**
227 | * @class Ext.ux.Exporter.ExcelFormatter
228 | * @extends Ext.ux.Exporter.Formatter
229 | * Specialised Format class for outputting .xls files
230 | */
231 | Ext.ux.Exporter.ExcelFormatter = Ext.extend(Ext.ux.Exporter.Formatter, {
232 |
233 | format: function(store, config) {
234 | var workbook = new Ext.ux.Exporter.ExcelFormatter.Workbook(config);
235 | workbook.addWorksheet(store, config || {});
236 |
237 | return workbook.render();
238 | }
239 | });
240 |
241 | /**
242 | * @class Ext.ux.Exporter.ExcelFormatter.Workbook
243 | * @extends Object
244 | * Represents an Excel workbook
245 | */
246 | Ext.ux.Exporter.ExcelFormatter.Workbook = Ext.extend(Object, {
247 |
248 | constructor: function(config) {
249 | config = config || {};
250 |
251 | Ext.apply(this, config, {
252 | /**
253 | * @property title
254 | * @type String
255 | * The title of the workbook (defaults to "Workbook")
256 | */
257 | title: "Workbook",
258 |
259 | /**
260 | * @property worksheets
261 | * @type Array
262 | * The array of worksheets inside this workbook
263 | */
264 | worksheets: [],
265 |
266 | /**
267 | * @property compileWorksheets
268 | * @type Array
269 | * Array of all rendered Worksheets
270 | */
271 | compiledWorksheets: [],
272 |
273 | /**
274 | * @property cellBorderColor
275 | * @type String
276 | * The colour of border to use for each Cell
277 | */
278 | cellBorderColor: "#e4e4e4",
279 |
280 | /**
281 | * @property styles
282 | * @type Array
283 | * The array of Ext.ux.Exporter.ExcelFormatter.Style objects attached to this workbook
284 | */
285 | styles: [],
286 |
287 | /**
288 | * @property compiledStyles
289 | * @type Array
290 | * Array of all rendered Ext.ux.Exporter.ExcelFormatter.Style objects for this workbook
291 | */
292 | compiledStyles: [],
293 |
294 | /**
295 | * @property hasDefaultStyle
296 | * @type Boolean
297 | * True to add the default styling options to all cells (defaults to true)
298 | */
299 | hasDefaultStyle: true,
300 |
301 | /**
302 | * @property hasStripeStyles
303 | * @type Boolean
304 | * True to add the striping styles (defaults to true)
305 | */
306 | hasStripeStyles: true,
307 |
308 | windowHeight : 9000,
309 | windowWidth : 50000,
310 | protectStructure: false,
311 | protectWindows : false
312 | });
313 |
314 | if (this.hasDefaultStyle) this.addDefaultStyle();
315 | if (this.hasStripeStyles) this.addStripedStyles();
316 |
317 | this.addTitleStyle();
318 | this.addHeaderStyle();
319 | },
320 |
321 | render: function() {
322 | this.compileStyles();
323 | this.joinedCompiledStyles = this.compiledStyles.join("");
324 |
325 | this.compileWorksheets();
326 | this.joinedWorksheets = this.compiledWorksheets.join("");
327 |
328 | return this.tpl.apply(this);
329 | },
330 |
331 | /**
332 | * Adds a worksheet to this workbook based on a store and optional config
333 | * @param {Ext.data.Store} store The store to initialize the worksheet with
334 | * @param {Object} config Optional config object
335 | * @return {Ext.ux.Exporter.ExcelFormatter.Worksheet} The worksheet
336 | */
337 | addWorksheet: function(store, config) {
338 | var worksheet = new Ext.ux.Exporter.ExcelFormatter.Worksheet(store, config);
339 |
340 | this.worksheets.push(worksheet);
341 |
342 | return worksheet;
343 | },
344 |
345 | /**
346 | * Adds a new Ext.ux.Exporter.ExcelFormatter.Style to this Workbook
347 | * @param {Object} config The style config, passed to the Style constructor (required)
348 | */
349 | addStyle: function(config) {
350 | var style = new Ext.ux.Exporter.ExcelFormatter.Style(config || {});
351 |
352 | this.styles.push(style);
353 |
354 | return style;
355 | },
356 |
357 | /**
358 | * Compiles each Style attached to this Workbook by rendering it
359 | * @return {Array} The compiled styles array
360 | */
361 | compileStyles: function() {
362 | this.compiledStyles = [];
363 |
364 | Ext.each(this.styles, function(style) {
365 | this.compiledStyles.push(style.render());
366 | }, this);
367 |
368 | return this.compiledStyles;
369 | },
370 |
371 | /**
372 | * Compiles each Worksheet attached to this Workbook by rendering it
373 | * @return {Array} The compiled worksheets array
374 | */
375 | compileWorksheets: function() {
376 | this.compiledWorksheets = [];
377 |
378 | Ext.each(this.worksheets, function(worksheet) {
379 | this.compiledWorksheets.push(worksheet.render());
380 | }, this);
381 |
382 | return this.compiledWorksheets;
383 | },
384 |
385 | tpl: new Ext.XTemplate(
386 | '',
387 | '',
388 | '',
389 | '{title}',
390 | '',
391 | '',
392 | '{windowHeight}',
393 | '{windowWidth}',
394 | '{protectStructure}',
395 | '{protectWindows}',
396 | '',
397 | '',
398 | '{joinedCompiledStyles}',
399 | '',
400 | '{joinedWorksheets}',
401 | ''
402 | ),
403 |
404 | /**
405 | * Adds the default Style to this workbook. This sets the default font face and size, as well as cell borders
406 | */
407 | addDefaultStyle: function() {
408 | var borderProperties = [
409 | {name: "Color", value: this.cellBorderColor},
410 | {name: "Weight", value: "1"},
411 | {name: "LineStyle", value: "Continuous"}
412 | ];
413 |
414 | this.addStyle({
415 | id: 'Default',
416 | attributes: [
417 | {
418 | name: "Alignment",
419 | properties: [
420 | {name: "Vertical", value: "Top"},
421 | {name: "WrapText", value: "1"}
422 | ]
423 | },
424 | {
425 | name: "Font",
426 | properties: [
427 | {name: "FontName", value: "arial"},
428 | {name: "Size", value: "10"}
429 | ]
430 | },
431 | {name: "Interior"}, {name: "NumberFormat"}, {name: "Protection"},
432 | {
433 | name: "Borders",
434 | children: [
435 | {
436 | name: "Border",
437 | properties: [{name: "Position", value: "Top"}].concat(borderProperties)
438 | },
439 | {
440 | name: "Border",
441 | properties: [{name: "Position", value: "Bottom"}].concat(borderProperties)
442 | },
443 | {
444 | name: "Border",
445 | properties: [{name: "Position", value: "Left"}].concat(borderProperties)
446 | },
447 | {
448 | name: "Border",
449 | properties: [{name: "Position", value: "Right"}].concat(borderProperties)
450 | }
451 | ]
452 | }
453 | ]
454 | });
455 | },
456 |
457 | addTitleStyle: function() {
458 | this.addStyle({
459 | id: "title",
460 | attributes: [
461 | {name: "Borders"},
462 | {name: "Font"},
463 | {
464 | name: "NumberFormat",
465 | properties: [
466 | {name: "Format", value: "@"}
467 | ]
468 | },
469 | {
470 | name: "Alignment",
471 | properties: [
472 | {name: "WrapText", value: "1"},
473 | {name: "Horizontal", value: "Center"},
474 | {name: "Vertical", value: "Center"}
475 | ]
476 | }
477 | ]
478 | });
479 | },
480 |
481 | addHeaderStyle: function() {
482 | this.addStyle({
483 | id: "headercell",
484 | attributes: [
485 | {
486 | name: "Font",
487 | properties: [
488 | {name: "Bold", value: "1"},
489 | {name: "Size", value: "10"}
490 | ]
491 | },
492 | {
493 | name: "Interior",
494 | properties: [
495 | {name: "Pattern", value: "Solid"},
496 | {name: "Color", value: "#A3C9F1"}
497 | ]
498 | },
499 | {
500 | name: "Alignment",
501 | properties: [
502 | {name: "WrapText", value: "1"},
503 | {name: "Horizontal", value: "Center"}
504 | ]
505 | }
506 | ]
507 | });
508 | },
509 |
510 | /**
511 | * Adds the default striping styles to this workbook
512 | */
513 | addStripedStyles: function() {
514 | this.addStyle({
515 | id: "even",
516 | attributes: [
517 | {
518 | name: "Interior",
519 | properties: [
520 | {name: "Pattern", value: "Solid"},
521 | {name: "Color", value: "#CCFFFF"}
522 | ]
523 | }
524 | ]
525 | });
526 |
527 | this.addStyle({
528 | id: "odd",
529 | attributes: [
530 | {
531 | name: "Interior",
532 | properties: [
533 | {name: "Pattern", value: "Solid"},
534 | {name: "Color", value: "#CCCCFF"}
535 | ]
536 | }
537 | ]
538 | });
539 |
540 | Ext.each(['even', 'odd'], function(parentStyle) {
541 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'date', "[ENG][$-409]dd\-mmm\-yyyy;@");
542 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'int', "0");
543 | this.addChildNumberFormatStyle(parentStyle, parentStyle + 'float', "0.00");
544 | }, this);
545 | },
546 |
547 | /**
548 | * Private convenience function to easily add a NumberFormat style for a given parentStyle
549 | * @param {String} parentStyle The ID of the parentStyle Style
550 | * @param {String} id The ID of the new style
551 | * @param {String} value The value of the NumberFormat's Format property
552 | */
553 | addChildNumberFormatStyle: function(parentStyle, id, value) {
554 | this.addStyle({
555 | id: id,
556 | parentStyle: "even",
557 | attributes: [
558 | {
559 | name: "NumberFormat",
560 | properties: [{name: "Format", value: value}]
561 | }
562 | ]
563 | });
564 | }
565 | });
566 |
567 | /**
568 | * @class Ext.ux.Exporter.ExcelFormatter.Worksheet
569 | * @extends Object
570 | * Represents an Excel worksheet
571 | * @cfg {Ext.data.Store} store The store to use (required)
572 | */
573 | Ext.ux.Exporter.ExcelFormatter.Worksheet = Ext.extend(Object, {
574 |
575 | constructor: function(store, config) {
576 | config = config || {};
577 |
578 | this.store = store;
579 |
580 | Ext.applyIf(config, {
581 | hasTitle : true,
582 | hasHeadings: true,
583 | stripeRows : true,
584 |
585 | title : "Workbook",
586 | columns : store.fields == undefined ? {} : store.fields.items
587 | });
588 |
589 | Ext.apply(this, config);
590 |
591 | Ext.ux.Exporter.ExcelFormatter.Worksheet.superclass.constructor.apply(this, arguments);
592 | },
593 |
594 | /**
595 | * @property dateFormatString
596 | * @type String
597 | * String used to format dates (defaults to "Y-m-d"). All other data types are left unmolested
598 | */
599 | dateFormatString: "Y-m-d",
600 |
601 | worksheetTpl: new Ext.XTemplate(
602 | '',
603 | '',
604 | '',
605 | '',
606 | '',
607 | '{columns}',
608 | '',
609 | '',
610 | '',
611 | '{title}',
612 | '',
613 | '',
614 | '',
615 | '',
616 | '{header}',
617 | '',
618 | '{rows}',
619 | '',
620 | '',
621 | '',
622 | '',
623 | '',
624 | '',
625 | '',
626 | '',
627 | '',
628 | 'Blank',
629 | '1',
630 | '32767',
631 | '',
632 | '600',
633 | '',
634 | '',
635 | '',
636 | 'False',
637 | 'False',
638 | '',
639 | ''
640 | ),
641 |
642 | /**
643 | * Builds the Worksheet XML
644 | * @param {Ext.data.Store} store The store to build from
645 | */
646 | render: function(store) {
647 | return this.worksheetTpl.apply({
648 | header : this.buildHeader(),
649 | columns : this.buildColumns().join(""),
650 | rows : this.buildRows().join(""),
651 | colCount: this.columns.length,
652 | rowCount: this.store.getCount() + 2,
653 | title : this.title
654 | });
655 | },
656 |
657 | buildColumns: function() {
658 | var cols = [];
659 |
660 | Ext.each(this.columns, function(column) {
661 | cols.push(this.buildColumn());
662 | }, this);
663 |
664 | return cols;
665 | },
666 |
667 | buildColumn: function(width) {
668 | return String.format('', width || 164);
669 | },
670 |
671 | buildRows: function() {
672 | var rows = [];
673 |
674 | this.store.each(function(record, index) {
675 | rows.push(this.buildRow(record, index));
676 | }, this);
677 |
678 | return rows;
679 | },
680 |
681 | buildHeader: function() {
682 | var cells = [];
683 |
684 | Ext.each(this.columns, function(col) {
685 | var title;
686 |
687 | if (col.header != undefined) {
688 | title = col.header;
689 | } else {
690 | //make columns taken from Record fields (e.g. with a col.name) human-readable
691 | title = col.name.replace(/_/g, " ");
692 | title = title.charAt(0).toUpperCase() + title.substr(1).toLowerCase();
693 | }
694 |
695 | cells.push(String.format('{0}', title));
696 | }, this);
697 |
698 | return cells.join("");
699 | },
700 |
701 | buildRow: function(record, index) {
702 | var style,
703 | cells = [];
704 | if (this.stripeRows === true) style = index % 2 == 0 ? 'even' : 'odd';
705 |
706 | Ext.each(this.columns, function(col) {
707 | var name = col.name || col.dataIndex;
708 |
709 | //if given a renderer via a ColumnModel, use it and ensure data type is set to String
710 | if (Ext.isFunction(col.renderer)) {
711 | var value = col.renderer(record.get(name), null, record),
712 | type = "String";
713 | } else {
714 | var value = record.get(name),
715 | type = this.typeMappings[col.type || record.fields.item(name).type];
716 | }
717 |
718 | cells.push(this.buildCell(value, type, style).render());
719 | }, this);
720 |
721 | return String.format("{0}", cells.join(""));
722 | },
723 |
724 | buildCell: function(value, type, style) {
725 | if (type == "DateTime" && Ext.isFunction(value.format)) value = value.format(this.dateFormatString);
726 |
727 | return new Ext.ux.Exporter.ExcelFormatter.Cell({
728 | value: value,
729 | type : type,
730 | style: style
731 | });
732 | },
733 |
734 | /**
735 | * @property typeMappings
736 | * @type Object
737 | * Mappings from Ext.data.Record types to Excel types
738 | */
739 | typeMappings: {
740 | 'int' : "Number",
741 | 'string': "String",
742 | 'float' : "Number",
743 | 'date' : "DateTime"
744 | }
745 | });
746 |
747 | /**
748 | * @class Ext.ux.Exporter.ExcelFormatter.Cell
749 | * @extends Object
750 | * Represents a single cell in a worksheet
751 | */
752 | Ext.ux.Exporter.ExcelFormatter.Cell = Ext.extend(Object, {
753 |
754 | constructor: function(config) {
755 | Ext.applyIf(config, {
756 | type: "String"
757 | });
758 |
759 | Ext.apply(this, config);
760 |
761 | Ext.ux.Exporter.ExcelFormatter.Cell.superclass.constructor.apply(this, arguments);
762 | },
763 |
764 | render: function() {
765 | return this.tpl.apply(this);
766 | },
767 |
768 | tpl: new Ext.XTemplate(
769 | '',
770 | '',
771 | ''
772 | )
773 | });
774 |
775 | /**
776 | * @class Ext.ux.Exporter.ExcelFormatter.Style
777 | * @extends Object
778 | * Represents a style declaration for a Workbook (this is like defining CSS rules). Example:
779 | *
780 | * new Ext.ux.Exporter.ExcelFormatter.Style({
781 | * attributes: [
782 | * {
783 | * name: "Alignment",
784 | * properties: [
785 | * {name: "Vertical", value: "Top"},
786 | * {name: "WrapText", value: "1"}
787 | * ]
788 | * },
789 | * {
790 | * name: "Borders",
791 | * children: [
792 | * name: "Border",
793 | * properties: [
794 | * {name: "Color", value: "#e4e4e4"},
795 | * {name: "Weight", value: "1"}
796 | * ]
797 | * ]
798 | * }
799 | * ]
800 | * })
801 | *
802 | * @cfg {String} id The ID of this style (required)
803 | * @cfg {Array} attributes The attributes for this style
804 | * @cfg {String} parentStyle The (optional parentStyle ID)
805 | */
806 | Ext.ux.Exporter.ExcelFormatter.Style = Ext.extend(Object, {
807 |
808 | constructor: function(config) {
809 | config = config || {};
810 |
811 | Ext.apply(this, config, {
812 | parentStyle: '',
813 | attributes : []
814 | });
815 |
816 | Ext.ux.Exporter.ExcelFormatter.Style.superclass.constructor.apply(this, arguments);
817 |
818 | if (this.id == undefined) throw new Error("An ID must be provided to Style");
819 |
820 | this.preparePropertyStrings();
821 | },
822 |
823 | /**
824 | * Iterates over the attributes in this style, and any children they may have, creating property
825 | * strings on each suitable for use in the XTemplate
826 | */
827 | preparePropertyStrings: function() {
828 | Ext.each(this.attributes, function(attr, index) {
829 | this.attributes[index].propertiesString = this.buildPropertyString(attr);
830 | this.attributes[index].children = attr.children || [];
831 |
832 | Ext.each(attr.children, function(child, childIndex) {
833 | this.attributes[index].children[childIndex].propertiesString = this.buildPropertyString(child);
834 | }, this);
835 | }, this);
836 | },
837 |
838 | /**
839 | * Builds a concatenated property string for a given attribute, suitable for use in the XTemplate
840 | */
841 | buildPropertyString: function(attribute) {
842 | var propertiesString = "";
843 |
844 | Ext.each(attribute.properties || [], function(property) {
845 | propertiesString += String.format('ss:{0}="{1}" ', property.name, property.value);
846 | }, this);
847 |
848 | return propertiesString;
849 | },
850 |
851 | render: function() {
852 | return this.tpl.apply(this);
853 | },
854 |
855 | tpl: new Ext.XTemplate(
856 | '',
857 | '',
858 | '',
859 | '',
860 | '',
861 | '',
862 | '',
863 | '',
864 | '',
865 | '',
866 | '',
867 | '',
868 | '',
869 | '',
870 | '',
871 | '',
872 | '',
873 | '',
874 | ''
875 | )
876 | });
877 |
878 |
--------------------------------------------------------------------------------
/Exporter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter
3 | * @author Ed Spencer (http://edspencer.net)
4 | * Class providing a common way of downloading data in .xls or .csv format
5 | */
6 | Ext.ux.Exporter = function() {
7 | return {
8 | /**
9 | * Exports a grid, using the .xls formatter by default
10 | * @param {Ext.grid.GridPanel} grid The grid to export from
11 | * @param {Object} config Optional config settings for the formatter
12 | */
13 | exportGrid: function(grid, formatter, config) {
14 | config = config || {};
15 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
16 |
17 | Ext.applyIf(config, {
18 | title : grid.title,
19 | columns: grid.getColumnModel().config
20 | });
21 |
22 | return Base64.encode(formatter.format(grid.store, config));
23 | },
24 |
25 | exportStore: function(store, formatter, config) {
26 | config = config || {};
27 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
28 |
29 | Ext.applyIf(config, {
30 | columns: config.store.fields.items
31 | });
32 |
33 | return Base64.encode(formatter.format(store, config));
34 | },
35 |
36 | exportTree: function(tree, formatter, config) {
37 | config = config || {};
38 | formatter = formatter || new Ext.ux.Exporter.ExcelFormatter();
39 |
40 | var store = tree.store || config.store;
41 |
42 | Ext.applyIf(config, {
43 | title: tree.title
44 | });
45 |
46 | return Base64.encode(formatter.format(store, config));
47 | }
48 | };
49 | }();
--------------------------------------------------------------------------------
/Formatter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.Exporter.Formatter
3 | * @author Ed Spencer (http://edspencer.net)
4 | * @cfg {Ext.data.Store} store The store to export
5 | */
6 | Ext.ux.Exporter.Formatter = function(config) {
7 | config = config || {};
8 |
9 | Ext.applyIf(config, {
10 |
11 | });
12 | };
13 |
14 | Ext.ux.Exporter.Formatter.prototype = {
15 | /**
16 | * Performs the actual formatting. This must be overridden by a subclass
17 | */
18 | format: Ext.emptyFn
19 | };
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ed Spencer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.textile:
--------------------------------------------------------------------------------
1 | h1. Ext.ux.Exporter
2 |
3 | h2. About
4 |
5 | p. Exporter is a generic export class which takes any Ext.data.Store-based component (e.g. grids and similar) and exports the data in any format.
6 |
7 | p. Exporter works completely client-side. It uses a Formatter class to generate a document (.xls, .csv etc) and then redirects the user's browser to a data url so that they can view or download it.
8 |
9 | h2. Installation
10 |
11 | p. Download the latest version in any of the following ways:
12 |
13 | * git clone git://github.com/edspencer/Ext.ux.Exporter.git
14 | * Download from http://github.com/edspencer/Ext.ux.Exporter/zipball/master
15 | * Copy/paste Exporter-all.js from http://github.com/edspencer/Ext.ux.Exporter/blob/master/Exporter-all.js
16 |
17 | p. To install Ext.ux.Exporter, just include Exporter-all.js somewhere on your page:
18 |
19 | bc.
20 |
21 | h2. Usage
22 |
23 | p. The most common use case for the Exporter is exporting a grid to Excel, which is as simple as doing the following:
24 |
25 | bc.. var grid = new Ext.grid.GridPanel({
26 | store: someStore,
27 | tbar : [
28 | {
29 | xtype: 'exportbutton',
30 | store: someStore
31 | }
32 | ],
33 | //your normal grid config goes here
34 | });
35 |
36 | p. The provided 'exportbutton' is just a specialised Ext.Button subclass which redirects the user to the generated data url. If you want to provide your own implementation here, just check out the Button.js code (it's ridiculously simple).
37 |
38 | h2. Example
39 |
40 | Download or clone the code (see Installation section for details) and then drag the example/index.html file into any browser to see an example of the Exporter at work.
--------------------------------------------------------------------------------
/build:
--------------------------------------------------------------------------------
1 | # This file defines the order in which the plugin files must be loaded.
2 | # It is used by the Ext MVC's ruby plugin builder, which which can be invoked from your application's base directory like this:
3 |
4 | # ruby script/plugin build Exporter
5 |
6 | # This will generate a file named Exporter-all.js in this directory, which is just a concatenation of all the files you list here
7 | # NOTE: This is only used by the plugin builder, not the app itself. You must always ensure that your plugin is compiled to
8 | # Exporter-all.js by some other means if you do not use the plugin builder, as it is that file that will be loaded by your app
9 |
10 | Base64.js
11 |
12 | Exporter.js
13 | Button.js
14 |
15 | Formatter.js
16 |
17 | ExcelFormatter/ExcelFormatter.js
18 | ExcelFormatter/Workbook.js
19 | ExcelFormatter/Worksheet.js
20 | ExcelFormatter/Cell.js
21 | ExcelFormatter/Style.js
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 | Ext.ux.Exporter Example
9 |
10 |
11 |
12 |
13 |
14 |
15 | Loading...
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
112 |
113 |
--------------------------------------------------------------------------------
/example/style.css:
--------------------------------------------------------------------------------
1 | #loading-mask {
2 | position: absolute;
3 | left: 0;
4 | top: 0;
5 | width: 100%;
6 | height: 100%;
7 | z-index: 21000; /* Normal loading masks are 20001 */
8 | background-color: white;
9 | }
10 |
11 | #loading {
12 | position: absolute;
13 | left: 50%;
14 | top: 50%;
15 | padding: 2px;
16 | z-index: 21001;
17 | height: auto;
18 | margin: -35px 0 0 -30px;
19 | }
20 |
21 | #loading .loading-indicator {
22 | background: url(http://extjs.com/deploy/dev/docs/resources/extanim32.gif) no-repeat;
23 | color: #555;
24 | font: bold 13px tahoma,arial,helvetica;
25 | padding: 8px 42px;
26 | margin: 0;
27 | text-align: center;
28 | height: auto;
29 | }
--------------------------------------------------------------------------------