-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathPrinter-all.js
375 lines (325 loc) · 11.9 KB
/
Printer-all.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/**
* @class GetIt.GridPrinter
* @author Ed Spencer (edward@domine.co.uk)
* Class providing a common way of printing Ext.Components. Ext.ux.Printer.print delegates the printing to a specialised
* renderer class (each of which subclasses Ext.ux.Printer.BaseRenderer), based on the xtype of the component.
* Each renderer is registered with an xtype, and is used if the component to print has that xtype.
*
* See the files in the renderers directory to customise or to provide your own renderers.
*
* Usage example:
*
* var grid = new Ext.grid.GridPanel({
* colModel: //some column model,
* store : //some store
* });
*
* Ext.ux.Printer.print(grid);
*
*/
Ext.ux.Printer = function() {
return {
/**
* @property renderers
* @type Object
* An object in the form {xtype: RendererClass} which is manages the renderers registered by xtype
*/
renderers: {},
/**
* Registers a renderer function to handle components of a given xtype
* @param {String} xtype The component xtype the renderer will handle
* @param {Function} renderer The renderer to invoke for components of this xtype
*/
registerRenderer: function(xtype, renderer) {
this.renderers[xtype] = new (renderer)();
},
/**
* Returns the registered renderer for a given xtype
* @param {String} xtype The component xtype to find a renderer for
* @return {Object/undefined} The renderer instance for this xtype, or null if not found
*/
getRenderer: function(xtype) {
return this.renderers[xtype];
},
/**
* Prints the passed grid. Reflects on the grid's column model to build a table, and fills it using the store
* @param {Ext.Component} component The component to print
*/
print: function(component) {
var xtypes = component.getXTypes().split('/');
//iterate backwards over the xtypes of this component, dispatching to the most specific renderer
for (var i = xtypes.length - 1; i >= 0; i--){
var xtype = xtypes[i],
renderer = this.getRenderer(xtype);
if (renderer != undefined) {
renderer.print(component);
break;
}
}
}
};
}();
/**
* Override how getXTypes works so that it doesn't require that every single class has
* an xtype registered for it.
*/
Ext.override(Ext.Component, {
getXTypes : function(){
var tc = this.constructor;
if(!tc.xtypes){
var c = [], sc = this;
while(sc){ //was: while(sc && sc.constructor.xtype) {
var xtype = sc.constructor.xtype;
if (xtype != undefined) c.unshift(xtype);
sc = sc.constructor.superclass;
}
tc.xtypeChain = c;
tc.xtypes = c.join('/');
}
return tc.xtypes;
}
});
/**
* @class Ext.ux.Printer.BaseRenderer
* @extends Object
* @author Ed Spencer
* Abstract base renderer class. Don't use this directly, use a subclass instead
*/
Ext.ux.Printer.BaseRenderer = Ext.extend(Object, {
/**
* Prints the component
* @param {Ext.Component} component The component to print
*/
print: function(component) {
var name = component && component.getXType
? String.format("print_{0}_{1}", component.getXType(), component.id)
: "print";
var win = window.open('', name);
win.document.write(this.generateHTML(component));
win.document.close();
win.print();
win.close();
},
/**
* Generates the HTML Markup which wraps whatever this.generateBody produces
* @param {Ext.Component} component The component to generate HTML for
* @return {String} An HTML fragment to be placed inside the print window
*/
generateHTML: function(component) {
return new Ext.XTemplate(
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'<html>',
'<head>',
'<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
'<link href="' + this.stylesheetPath + '" rel="stylesheet" type="text/css" media="screen,print" />',
'<title>' + this.getTitle(component) + '</title>',
'</head>',
'<body>',
this.generateBody(component),
'</body>',
'</html>'
).apply(this.prepareData(component));
},
/**
* Returns the HTML that will be placed into the print window. This should produce HTML to go inside the
* <body> element only, as <head> is generated in the print function
* @param {Ext.Component} component The component to render
* @return {String} The HTML fragment to place inside the print window's <body> element
*/
generateBody: Ext.emptyFn,
/**
* Prepares data suitable for use in an XTemplate from the component
* @param {Ext.Component} component The component to acquire data from
* @return {Array} An empty array (override this to prepare your own data)
*/
prepareData: function(component) {
return component;
},
/**
* Returns the title to give to the print window
* @param {Ext.Component} component The component to be printed
* @return {String} The window title
*/
getTitle: function(component) {
return typeof component.getTitle == 'function' ? component.getTitle() : (component.title || "Printing");
},
/**
* @property stylesheetPath
* @type String
* The path at which the print stylesheet can be found (defaults to 'stylesheets/print.css')
*/
stylesheetPath: 'stylesheets/print.css'
});
/**
* @class Ext.ux.Printer.ColumnTreeRenderer
* @extends Ext.ux.Printer.BaseRenderer
* @author Ed Spencer
* Helper class to easily print the contents of a column tree
*/
Ext.ux.Printer.ColumnTreeRenderer = Ext.extend(Ext.ux.Printer.BaseRenderer, {
/**
* Generates the body HTML for the tree
* @param {Ext.tree.ColumnTree} tree The tree to print
*/
generateBody: function(tree) {
var columns = this.getColumns(tree);
//use the headerTpl and bodyTpl XTemplates to create the main XTemplate below
var headings = this.headerTpl.apply(columns);
var body = this.bodyTpl.apply(columns);
return String.format('<table>{0}<tpl for=".">{1}</tpl></table>', headings, body);
},
/**
* Returns the array of columns from a tree
* @param {Ext.tree.ColumnTree} tree The tree to get columns from
* @return {Array} The array of tree columns
*/
getColumns: function(tree) {
return tree.columns;
},
/**
* Descends down the tree from the root, creating an array of data suitable for use in an XTemplate
* @param {Ext.tree.ColumnTree} tree The column tree
* @return {Array} Data suitable for use in the body XTemplate
*/
prepareData: function(tree) {
var root = tree.root,
data = [],
cols = this.getColumns(tree),
padding = this.indentPadding;
var f = function(node) {
if (node.hidden === true || node.isHiddenRoot() === true) return;
var row = Ext.apply({depth: node.getDepth() * padding}, node.attributes);
Ext.iterate(row, function(key, value) {
Ext.each(cols, function(column) {
if (column.dataIndex == key) {
row[key] = column.renderer ? column.renderer(value) : value;
}
}, this);
});
//the property used in the first column is renamed to 'text' in node.attributes, so reassign it here
row[this.getColumns(tree)[0].dataIndex] = node.attributes.text;
data.push(row);
};
root.cascade(f, this);
return data;
},
/**
* @property indentPadding
* @type Number
* Number of pixels to indent node by. This is multiplied by the node depth, so a node with node.getDepth() == 3 will
* be padded by 45 (or 3x your custom indentPadding)
*/
indentPadding: 15,
/**
* @property headerTpl
* @type Ext.XTemplate
* The XTemplate used to create the headings row. By default this just uses <th> elements, override to provide your own
*/
headerTpl: new Ext.XTemplate(
'<tr>',
'<tpl for=".">',
'<th width="{width}">{header}</th>',
'</tpl>',
'</tr>'
),
/**
* @property bodyTpl
* @type Ext.XTemplate
* The XTemplate used to create each row. This is used inside the 'print' function to build another XTemplate, to which the data
* are then applied (see the escaped dataIndex attribute here - this ends up as "{dataIndex}")
*/
bodyTpl: new Ext.XTemplate(
'<tr>',
'<tpl for=".">',
'<td style="padding-left: {[xindex == 1 ? "\\{depth\\}" : "0"]}px">\{{dataIndex}\}</td>',
'</tpl>',
'</tr>'
)
});
Ext.ux.Printer.registerRenderer('columntree', Ext.ux.Printer.ColumnTreeRenderer);
/**
* @class Ext.ux.Printer.GridPanelRenderer
* @extends Ext.ux.Printer.BaseRenderer
* @author Ed Spencer
* Helper class to easily print the contents of a grid. Will open a new window with a table where the first row
* contains the headings from your column model, and with a row for each item in your grid's store. When formatted
* with appropriate CSS it should look very similar to a default grid. If renderers are specified in your column
* model, they will be used in creating the table. Override headerTpl and bodyTpl to change how the markup is generated
*/
Ext.ux.Printer.GridPanelRenderer = Ext.extend(Ext.ux.Printer.BaseRenderer, {
/**
* Generates the body HTML for the grid
* @param {Ext.grid.GridPanel} grid The grid to print
*/
generateBody: function(grid) {
var columns = this.getColumns(grid);
//use the headerTpl and bodyTpl XTemplates to create the main XTemplate below
var headings = this.headerTpl.apply(columns);
var body = this.bodyTpl.apply(columns);
return String.format('<table>{0}<tpl for=".">{1}</tpl></table>', headings, body);
},
/**
* Prepares data from the grid for use in the XTemplate
* @param {Ext.grid.GridPanel} grid The grid panel
* @return {Array} Data suitable for use in the XTemplate
*/
prepareData: function(grid) {
//We generate an XTemplate here by using 2 intermediary XTemplates - one to create the header,
//the other to create the body (see the escaped {} below)
var columns = this.getColumns(grid);
//build a useable array of store data for the XTemplate
var data = [];
grid.store.data.each(function(item) {
var convertedData = {};
//apply renderers from column model
Ext.iterate(item.data, function(key, value) {
Ext.each(columns, function(column) {
if (column.dataIndex == key) {
convertedData[key] = column.renderer ? column.renderer(value, null, item) : value;
return false;
}
}, this);
});
data.push(convertedData);
});
return data;
},
/**
* Returns the array of columns from a grid
* @param {Ext.grid.GridPanel} grid The grid to get columns from
* @return {Array} The array of grid columns
*/
getColumns: function(grid) {
var columns = [];
Ext.each(grid.getColumnModel().config, function(col) {
if (col.hidden != true) columns.push(col);
}, this);
return columns;
},
/**
* @property headerTpl
* @type Ext.XTemplate
* The XTemplate used to create the headings row. By default this just uses <th> elements, override to provide your own
*/
headerTpl: new Ext.XTemplate(
'<tr>',
'<tpl for=".">',
'<th>{header}</th>',
'</tpl>',
'</tr>'
),
/**
* @property bodyTpl
* @type Ext.XTemplate
* The XTemplate used to create each row. This is used inside the 'print' function to build another XTemplate, to which the data
* are then applied (see the escaped dataIndex attribute here - this ends up as "{dataIndex}")
*/
bodyTpl: new Ext.XTemplate(
'<tr>',
'<tpl for=".">',
'<td>\{{dataIndex}\}</td>',
'</tpl>',
'</tr>'
)
});
Ext.ux.Printer.registerRenderer('grid', Ext.ux.Printer.GridPanelRenderer);