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';
9 const HATCH_FILE_WRITER_PRINTER = 'hatch_file_writer';
13 templateUrl: './print.component.html'
16 export class PrintComponent implements OnInit {
18 // Template that requires local processing
19 template: TemplateRef<any>;
21 // Context data used for processing the template.
24 // Insertion point for externally-compiled templates
25 htmlContainer: Element;
29 printQueue: PrintRequest[];
31 // True if Hatch printing is enabled and we're able to talk to Hatch.
32 useHatchPrinting: boolean = null;
35 private renderer: Renderer2,
36 private elm: ElementRef,
37 private store: StoreService,
38 private serverStore: ServerStoreService,
39 private h2txt: HtmlToTxtService,
40 private hatch: HatchService,
41 private toast: ToastService,
42 private strings: StringService,
43 private printer: PrintService) {
44 this.isPrinting = false;
49 this.printer.onPrintRequest$.subscribe(
50 printReq => this.handlePrintRequest(printReq));
53 this.renderer.selectRootElement('#eg-print-html-container');
57 // Returns promise of true if Hatch should be used for printing.
58 // To avoid race conditions, always check this inline before
59 // relaying print requests.
60 checkHatchEnabled(): Promise<boolean> {
61 if (this.useHatchPrinting !== null) {
62 return Promise.resolve(this.useHatchPrinting);
65 return this.serverStore.getItem('eg.hatch.enable.printing')
66 .then(use => this.useHatchPrinting = (use && this.hatch.connect()));
69 handlePrintRequest(printReq: PrintRequest) {
71 if (this.isPrinting) {
72 // Avoid print collisions by queuing requests as needed.
73 this.printQueue.push(printReq);
77 this.isPrinting = true;
79 this.applyTemplate(printReq).then(() => {
80 // Give templates a chance to render before printing
82 this.dispatchPrint(printReq).then(_ => this.reset());
87 applyTemplate(printReq: PrintRequest): Promise<any> {
89 if (printReq.template) {
90 // Local Angular template.
91 this.template = printReq.template;
92 this.context = {$implicit: printReq.contextData};
93 return Promise.resolve();
100 promise = Promise.resolve();
102 } else if (printReq.templateName || printReq.templateId) {
103 // Server-compiled template
105 promise = this.printer.compileRemoteTemplate(printReq).then(
107 printReq.text = response.content;
108 printReq.contentType = response.contentType;
112 if (err && err.notFound) {
114 this.strings.interpolate(
115 'eg.print.template.not_found',
116 {name: printReq.templateName}
117 ).then(msg => this.toast.danger(msg));
121 console.error('Print generation failed', printReq);
123 this.strings.interpolate(
124 'eg.print.template.error',
125 {name: printReq.templateName, id: printReq.templateId}
126 ).then(msg => this.toast.danger(msg));
129 return Promise.reject(new Error(
130 'Error compiling server-hosted print template'));
135 console.error('Cannot find template', printReq);
136 return Promise.reject(new Error('Cannot find print template'));
139 return promise.then(() => {
141 return this.checkHatchEnabled().then(enabled => {
143 // Insert HTML into the browser DOM for in-browser printing.
144 if (printReq.text && !enabled) {
146 if (printReq.contentType === 'text/plain') {
147 // Wrap text/plain content in pre's to prevent
148 // unintended html formatting.
149 printReq.text = `<pre>${printReq.text}</pre>`;
152 this.htmlContainer.innerHTML = printReq.text;
158 // Clear the print data
160 this.isPrinting = false;
161 this.template = null;
163 this.htmlContainer.innerHTML = '';
165 if (this.printQueue.length) {
166 this.handlePrintRequest(this.printQueue.pop());
170 dispatchPrint(printReq: PrintRequest): Promise<any> {
172 if (!printReq.text) {
174 // Extract the print container div from our component markup.
176 this.elm.nativeElement.querySelector('#eg-print-container');
178 // Sometimes the results come from an externally-parsed HTML
179 // template, other times they come from an in-page template.
180 printReq.text = container.innerHTML;
183 // Retain a copy of each printed document in localStorage
184 // so it may be reprinted.
185 this.store.setLocalItem('eg.print.last_printed', {
186 content: printReq.text,
187 context: printReq.printContext,
188 content_type: printReq.contentType,
189 show_dialog: printReq.showDialog
192 return this.checkHatchEnabled().then(enabled => {
194 this.printViaHatch(printReq);
196 // Here the needed HTML is already in the page.
202 printViaHatch(printReq: PrintRequest) {
203 if (!printReq.contentType) {
204 printReq.contentType = 'text/html';
207 // Send a full HTML document to Hatch
208 let html = printReq.text;
209 if (printReq.contentType === 'text/html') {
210 html = `<html><body>${printReq.text}</body></html>`;
213 this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
216 let msg: HatchMessage;
218 if (config && config.printer === HATCH_FILE_WRITER_PRINTER) {
220 const text = printReq.contentType === 'text/plain' ?
221 html : this.h2txt.htmlToTxt(html);
223 msg = new HatchMessage({
225 key: `receipt.${printReq.printContext}.txt`,
232 msg = new HatchMessage({
235 settings: config || {},
236 contentType: 'text/html',
237 showDialog: printReq.showDialog
241 this.hatch.sendRequest(msg).then(
242 ok => console.debug('Print request succeeded'),
243 err => console.warn('Print request failed', err)