97f833309109101160314b45c004c375c2fc4d71
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / print.js
1 /**
2  * egPrint : manage print templates, process templates, print content
3  *
4  */
5 angular.module('egCoreMod')
6
7 .factory('egPrint',
8        ['$q','$window','$timeout','$http','egHatch','egAuth','egIDL','egOrg','egEnv',
9 function($q , $window , $timeout , $http , egHatch , egAuth , egIDL , egOrg , egEnv) {
10
11     var service = {};
12
13     service.template_base_path = 'share/print_templates/t_';
14
15     /*
16      * context  : 'default', 'receipt','label', etc. 
17      * scope    : data loaded into the template environment
18      * template : template name (e.g. 'checkout', 'transit_slip'
19      * content  : content to print.  If 'template' is set, content is
20      *            derived from the template.
21      * content_type : 'text/html', 'text/plain', 'text/csv'
22      * show_dialog  : boolean, if true, print dialog is shown.  This setting
23      *                only affects remote printers, since browser printers
24      *                do not allow such control
25      */
26     service.print = function(args) {
27         if (!args) return $q.when();
28
29         if (args.template) {
30             // fetch the template, then proceed to printing
31
32             return service.getPrintTemplate(args.template)
33             .then(function(content) {
34                 args.content = content;
35                 if (!args.content_type) args.content_type = 'text/html';
36                 service.getPrintTemplateContext(args.template)
37                 .then(function(context) {
38                     args.context = context;
39                     return service.print_content(args);
40                 });
41             });
42
43         } 
44
45         return service.print_content(args);
46     }
47
48     // add commonly used attributes to the print scope
49     service.fleshPrintScope = function(scope) {
50         if (!scope) scope = {};
51         scope.today = new Date().toISOString();
52         scope.staff = egIDL.toHash(egAuth.user());
53         scope.current_location = 
54             egIDL.toHash(egOrg.get(egAuth.user().ws_ou()));
55     }
56
57     service.last_print = {};
58
59     // Template has been fetched (or no template needed) 
60     // Process the template and send the result off to the printer.
61     service.print_content = function(args) {
62         service.fleshPrintScope(args.scope);
63
64         var promise = egHatch.usePrinting() ?
65             service.print_via_hatch(args) :
66             service.print_via_browser(args);
67
68         return promise['finally'](
69             function() { service.clear_print_content() });
70     }
71
72     service.print_via_hatch = function(args) {
73         var promise;
74
75         if (args.content_type == 'text/html') {
76             promise = service.ingest_print_content(
77                 args.content_type, args.content, args.scope);
78         } else {
79             // text content requires no compilation for remote printing.
80             promise = $q.when(args.content);
81         }
82
83         return promise.then(function(html) {
84             // For good measure, wrap the compiled HTML in container tags.
85             html = "<html><body>" + html + "</body></html>";
86             service.last_print.content = html;
87             service.last_print.context = args.context || 'default';
88             service.last_print.content_type = args.content_type;
89             service.last_print.show_dialog = args.show_dialog;
90
91             egHatch.setItem('eg.print.last_printed', service.last_print);
92
93             return service._remotePrint();
94         });
95     }
96
97     service._remotePrint = function () {
98         return egHatch.remotePrint(
99             service.last_print.context,
100             service.last_print.content_type,
101             service.last_print.content, 
102             service.last_print.show_dialog
103         );
104     }
105
106     service.print_via_browser = function(args) {
107         var type = args.content_type;
108         var content = args.content;
109         var printScope = args.scope;
110
111         if (type == 'text/csv' || type == 'text/plain') {
112             // preserve newlines, spaces, etc.
113             content = '<pre>' + content + '</pre>';
114         }
115
116         // Fetch the print CSS required for in-browser printing.
117         return $http.get(egEnv.basePath + 'css/print.css')
118         .then(function(response) {
119
120             // Add the bare CSS to the content
121             return '<style type="text/css" media="print">' +
122                   response.data +
123                   '</style>' +
124                   content;
125
126         }).then(function(content) {
127             service.last_print.content = content;
128             service.last_print.content_type = type;
129             service.last_print.printScope = printScope
130
131             egHatch.setItem('eg.print.last_printed', service.last_print);
132
133             // Ingest the content into the page DOM.
134             return service.ingest_print_content(
135                 service.last_print.content_type,
136                 service.last_print.content,
137                 service.last_print.printScope
138             );
139
140         }).then(function() { 
141             $window.print();
142         });
143     }
144
145     service.reprintLast = function () {
146         var deferred = $q.defer();
147         var promise = deferred.promise;
148         promise.finally( function() { service.clear_print_content() });
149
150         egHatch.getItem(
151             'eg.print.last_printed'
152         ).then(function (last) {
153             if (last && last.content) {
154                 service.last_print = last;
155
156                 if (egHatch.usePrinting()) {
157                     promise.then(function () {
158                         egHatch._remotePrint()
159                     });
160                 } else {
161                     promise.then(function () {
162                         service.ingest_print_content(
163                             service.last_print.content_type,
164                             service.last_print.content,
165                             service.last_print.printScope
166                         ).then(function() { $window.print() });
167                     });
168                 }
169                 return deferred.resolve();
170             } else {
171                 return deferred.reject();
172             }
173         });
174     }
175
176     // loads an HTML print template by name from the server
177     // If no template is available in local/hatch storage, 
178     // fetch the template as an HTML file from the server.
179     service.getPrintTemplate = function(name) {
180         var deferred = $q.defer();
181
182         egHatch.getItem('eg.print.template.' + name)
183         .then(function(html) {
184
185             if (html) {
186                 // we have a locally stored template
187                 deferred.resolve(html);
188                 return;
189             }
190
191             var path = service.template_base_path + name;
192             console.debug('fetching template ' + path);
193
194             $http.get(path)
195             .success(function(data) { deferred.resolve(data) })
196             .error(function() {
197                 console.error('unable to locate print template: ' + name);
198                 deferred.reject();
199             });
200         });
201
202         return deferred.promise;
203     }
204
205     service.storePrintTemplate = function(name, html) {
206         return egHatch.setItem('eg.print.template.' + name, html);
207     }
208
209     service.getPrintTemplateContext = function(name) {
210         var deferred = $q.defer();
211
212         egHatch.getItem('eg.print.template_context.' + name)
213         .then(
214             function(context) { deferred.resolve(context); },
215             function()        { deferred.resolve('default'); }
216         );
217
218         return deferred.promise;
219     }
220     service.storePrintTemplateContext = function(name, context) {
221         return egHatch.setItem('eg.print.template_context.' + name, context);
222     }
223
224     return service;
225 }])
226
227
228 /**
229  * Container for inserting print data into the browser page.
230  * On insert, $window.print() is called to print the data.
231  * The div housing eg-print-container must apply the correct
232  * print media CSS to ensure this content (and not the rest
233  * of the page) is printed.
234  *
235  * NOTE: There should only ever be 1 egPrintContainer instance per page.
236  * egPrintContainer attaches functions to the egPrint service with
237  * closures around the egPrintContainer instance's $scope (including its
238  * DOM element). Having multiple egPrintContainers could result in chaos.
239  */
240
241 .directive('egPrintContainer', ['$compile', function($compile) {
242     return {
243         restrict : 'AE',
244         scope : {}, // isolate our scope
245         link : function(scope, element, attrs) {
246             scope.elm = element;
247         },
248         controller : 
249                    ['$scope','$q','$window','$timeout','egHatch','egPrint','egEnv',
250             function($scope , $q , $window , $timeout , egHatch , egPrint , egEnv) {
251
252                 egPrint.clear_print_content = function() {
253                     $scope.elm.html('');
254                     $compile($scope.elm.contents())($scope.$new(true));
255                 }
256
257                 // Insert the printable content into the DOM.
258                 // For remote printing, this lets us exract the compiled HTML
259                 // from the DOM.
260                 // For local printing, this lets us print directly from the
261                 // DOM with print CSS.
262                 // Returns a promise reolved with the compiled HTML as a string.
263                 egPrint.ingest_print_content = function(type, content, printScope) {
264                     $scope.elm.html(content);
265
266                     var sub_scope = $scope.$new(true);
267                     angular.forEach(printScope, function(val, key) {
268                         sub_scope[key] = val;
269                     })
270
271                     var resp = $compile($scope.elm.contents())(sub_scope);
272
273
274                     var deferred = $q.defer();
275                     $timeout(function(){
276                         // give the $digest a chance to complete then resolve
277                         // with the compiled HTML from our print container
278                         deferred.resolve($scope.elm.html());
279                     });
280
281                     return deferred.promise;
282                 }
283             }
284         ]
285     }
286 }])
287