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 // Load the configured print context.
78 let promise = Promise.resolve();
79 if (printReq.templateName) {
80 promise = this.serverStore.getItem(
81 'eg.print.template_context.' + printReq.templateName)
84 printReq.printContext = setting;
89 this.isPrinting = true;
92 if (printReq.printContext === 'no-print') {
93 console.debug('Skipping print request with No-Print context');
98 this.applyTemplate(printReq).then(() => {
99 // Give templates a chance to render before printing
101 this.dispatchPrint(printReq).then(__ => this.reset());
107 applyTemplate(printReq: PrintRequest): Promise<any> {
109 if (printReq.template) {
110 // Local Angular template.
111 this.template = printReq.template;
112 this.context = {$implicit: printReq.contextData};
113 return Promise.resolve();
120 promise = Promise.resolve();
122 } else if (printReq.templateName || printReq.templateId) {
123 // Server-compiled template
125 promise = this.printer.compileRemoteTemplate(printReq).then(
127 printReq.text = response.content;
128 printReq.contentType = response.contentType;
132 if (err && err.notFound) {
134 this.strings.interpolate(
135 'eg.print.template.not_found',
136 {name: printReq.templateName}
137 ).then(msg => this.toast.danger(msg));
141 console.error('Print generation failed', printReq);
143 this.strings.interpolate(
144 'eg.print.template.error',
145 {name: printReq.templateName, id: printReq.templateId}
146 ).then(msg => this.toast.danger(msg));
149 return Promise.reject(new Error(
150 'Error compiling server-hosted print template'));
155 console.error('Cannot find template', printReq);
156 return Promise.reject(new Error('Cannot find print template'));
159 return promise.then(() => {
161 return this.checkHatchEnabled().then(enabled => {
163 // Insert HTML into the browser DOM for in-browser printing.
164 if (printReq.text && !enabled) {
166 if (printReq.contentType === 'text/plain') {
167 // Wrap text/plain content in pre's to prevent
168 // unintended html formatting.
169 printReq.text = `<pre>${printReq.text}</pre>`;
172 this.htmlContainer.innerHTML = printReq.text;
178 // Clear the print data
180 this.isPrinting = false;
181 this.template = null;
183 this.htmlContainer.innerHTML = '';
185 if (this.printQueue.length) {
186 this.handlePrintRequest(this.printQueue.pop());
190 dispatchPrint(printReq: PrintRequest): Promise<any> {
192 if (!printReq.text) {
194 // Extract the print container div from our component markup.
196 this.elm.nativeElement.querySelector('#eg-print-container');
198 // Sometimes the results come from an externally-parsed HTML
199 // template, other times they come from an in-page template.
200 printReq.text = container.innerHTML;
203 // Retain a copy of each printed document in localStorage
204 // so it may be reprinted.
205 this.store.setLocalItem('eg.print.last_printed', {
206 content: printReq.text,
207 context: printReq.printContext,
208 content_type: printReq.contentType,
209 show_dialog: printReq.showDialog
212 return this.checkHatchEnabled().then(enabled => {
214 this.printViaHatch(printReq);
216 // Here the needed HTML is already in the page.
222 printViaHatch(printReq: PrintRequest) {
223 if (!printReq.contentType) {
224 printReq.contentType = 'text/html';
227 // Send a full HTML document to Hatch
228 let html = printReq.text;
229 if (printReq.contentType === 'text/html') {
230 html = `<html><body>${printReq.text}</body></html>`;
233 this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
236 let msg: HatchMessage;
238 if (config && config.printer === HATCH_FILE_WRITER_PRINTER) {
240 const text = printReq.contentType === 'text/plain' ?
241 html : this.h2txt.htmlToTxt(html);
243 msg = new HatchMessage({
245 key: `receipt.${printReq.printContext}.txt`,
252 msg = new HatchMessage({
255 settings: config || {},
256 contentType: 'text/html',
257 showDialog: printReq.showDialog
261 this.hatch.sendRequest(msg).then(
262 ok => console.debug('Print request succeeded'),
263 err => console.warn('Print request failed', err)