1 import {Component, OnInit, TemplateRef, ElementRef, Renderer2} from '@angular/core';
2 import {PrintService, PrintRequest} from './print.service';
3 import {StoreService} from '@eg/core/store.service';
4 import {ServerStoreService} from '@eg/core/server-store.service';
5 import {HatchService, HatchMessage} from '@eg/core/hatch.service';
6 import {ToastService} from '@eg/share/toast/toast.service';
7 import {StringService} from '@eg/share/string/string.service';
8 import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service';
10 const HATCH_FILE_WRITER_PRINTER = 'hatch_file_writer';
11 const HATCH_BROWSER_PRINTING_PRINTER = 'hatch_browser_printing';
15 templateUrl: './print.component.html'
18 export class PrintComponent implements OnInit {
20 // Template that requires local processing
21 template: TemplateRef<any>;
23 // Context data used for processing the template.
26 // Insertion point for externally-compiled templates
27 htmlContainer: Element;
31 printQueue: PrintRequest[];
33 // True if Hatch printing is enabled and we're able to talk to Hatch.
34 useHatchPrinting: boolean = null;
37 private renderer: Renderer2,
38 private elm: ElementRef,
39 private store: StoreService,
40 private serverStore: ServerStoreService,
41 private h2txt: HtmlToTxtService,
42 private hatch: HatchService,
43 private toast: ToastService,
44 private strings: StringService,
45 private printer: PrintService) {
46 this.isPrinting = false;
51 this.printer.onPrintRequest$.subscribe(
52 printReq => this.handlePrintRequest(printReq));
55 this.renderer.selectRootElement('#eg-print-html-container');
59 // Returns promise of true if Hatch should be used for printing.
60 // To avoid race conditions, always check this inline before
61 // relaying print requests.
62 checkHatchEnabled(): Promise<boolean> {
63 if (this.useHatchPrinting !== null) {
64 return Promise.resolve(this.useHatchPrinting);
67 return this.serverStore.getItem('eg.hatch.enable.printing')
68 .then(use => this.useHatchPrinting = (use && this.hatch.connect()));
71 // Resolves to true if a) Hatch is usable and b) the requested print
72 // context is not using the 'native brower printing' printer.
73 checkHatchEnabledForRequest(printReq: PrintRequest): Promise<boolean> {
74 return this.checkHatchEnabled().then(enabled => {
75 if (!enabled) { return false; }
77 return this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
81 config.printer !== HATCH_BROWSER_PRINTING_PRINTER
87 handlePrintRequest(printReq: PrintRequest) {
89 if (this.isPrinting) {
90 // Avoid print collisions by queuing requests as needed.
91 this.printQueue.push(printReq);
95 // Load the configured print context.
96 let promise = Promise.resolve();
97 if (printReq.templateName) {
98 promise = this.serverStore.getItem(
99 'eg.print.template_context.' + printReq.templateName)
102 printReq.printContext = setting;
107 this.isPrinting = true;
110 if (printReq.printContext === 'no-print') {
111 console.debug('Skipping print request with No-Print context');
116 this.applyTemplate(printReq).then(() => {
117 // Give templates a chance to render before printing
119 this.dispatchPrint(printReq).then(__ => {
121 this.printer.printJobQueued$.emit(printReq);
128 applyTemplate(printReq: PrintRequest): Promise<any> {
130 if (printReq.template) {
131 // Local Angular template.
132 this.template = printReq.template;
133 this.context = {$implicit: printReq.contextData};
134 return Promise.resolve();
141 promise = Promise.resolve();
143 } else if (printReq.templateName || printReq.templateId) {
144 // Server-compiled template
146 promise = this.printer.compileRemoteTemplate(printReq).then(
148 printReq.text = response.content;
149 printReq.contentType = response.contentType;
153 if (err && err.notFound) {
155 this.strings.interpolate(
156 'eg.print.template.not_found',
157 {name: printReq.templateName}
158 ).then(msg => this.toast.danger(msg));
162 console.error('Print generation failed', printReq);
164 this.strings.interpolate(
165 'eg.print.template.error',
166 {name: printReq.templateName, id: printReq.templateId}
167 ).then(msg => this.toast.danger(msg));
170 return Promise.reject(new Error(
171 'Error compiling server-hosted print template'));
176 console.error('Cannot find template', printReq);
177 return Promise.reject(new Error('Cannot find print template'));
180 return promise.then(() => {
182 return this.checkHatchEnabledForRequest(printReq).then(enabled => {
184 // Insert HTML into the browser DOM for in-browser printing.
185 if (printReq.text && !enabled) {
187 if (printReq.contentType === 'text/plain') {
188 // Wrap text/plain content in pre's to prevent
189 // unintended html formatting.
190 printReq.text = `<pre>${printReq.text}</pre>`;
193 this.htmlContainer.innerHTML = printReq.text;
199 // Clear the print data
201 this.isPrinting = false;
202 this.template = null;
204 this.htmlContainer.innerHTML = '';
206 if (this.printQueue.length) {
207 this.handlePrintRequest(this.printQueue.pop());
211 dispatchPrint(printReq: PrintRequest): Promise<any> {
213 if (!printReq.text) {
215 // Extract the print container div from our component markup.
217 this.elm.nativeElement.querySelector('#eg-print-container');
219 // Sometimes the results come from an externally-parsed HTML
220 // template, other times they come from an in-page template.
221 printReq.text = container.innerHTML;
224 // Retain a copy of each printed document in localStorage
225 // so it may be reprinted.
226 this.store.setLocalItem('eg.print.last_printed', {
227 content: printReq.text,
228 context: printReq.printContext,
229 content_type: printReq.contentType,
230 show_dialog: printReq.showDialog
233 return this.checkHatchEnabledForRequest(printReq).then(enabled => {
235 this.printViaHatch(printReq);
237 // Here the needed HTML is already in the page.
243 printViaHatch(printReq: PrintRequest) {
244 if (!printReq.contentType) {
245 printReq.contentType = 'text/html';
248 // Send a full HTML document to Hatch
249 let html = printReq.text;
250 if (printReq.contentType === 'text/html') {
251 html = `<html><body>${printReq.text}</body></html>`;
254 this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
257 let msg: HatchMessage;
259 if (config && config.printer === HATCH_FILE_WRITER_PRINTER) {
261 const text = printReq.contentType === 'text/plain' ?
262 html : this.h2txt.htmlToTxt(html);
264 msg = new HatchMessage({
266 key: `receipt.${printReq.printContext}.txt`,
273 msg = new HatchMessage({
276 settings: config || {},
277 contentType: 'text/html',
278 showDialog: printReq.showDialog
282 this.hatch.sendRequest(msg).then(
283 ok => console.debug('Print request succeeded'),
284 err => console.warn('Print request failed', err)