From 9896aa880576f5fffe3592aa030b6ee31bb21280 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 25 Jun 2018 11:37:35 -0400 Subject: [PATCH] LP#1775466 Print repairs; rm-ing legacy template parser Signed-off-by: Bill Erickson --- .../eg2/src/app/share/print/print.component.html | 17 +- .../src/eg2/src/app/share/print/print.component.ts | 36 ++- Open-ILS/src/eg2/src/app/staff/common.module.ts | 2 - .../src/app/staff/sandbox/sandbox.component.html | 4 - .../eg2/src/app/staff/sandbox/sandbox.component.ts | 72 +---- .../src/app/staff/share/legacy-template.service.ts | 295 --------------------- 6 files changed, 51 insertions(+), 375 deletions(-) delete mode 100644 Open-ILS/src/eg2/src/app/staff/share/legacy-template.service.ts diff --git a/Open-ILS/src/eg2/src/app/share/print/print.component.html b/Open-ILS/src/eg2/src/app/share/print/print.component.html index 2f72bda6fd..094e5b7b24 100644 --- a/Open-ILS/src/eg2/src/app/share/print/print.component.html +++ b/Open-ILS/src/eg2/src/app/share/print/print.component.html @@ -1,10 +1,15 @@ + - -
- - - - + + + + + + +
diff --git a/Open-ILS/src/eg2/src/app/share/print/print.component.ts b/Open-ILS/src/eg2/src/app/share/print/print.component.ts index 8102831587..09ef2447eb 100644 --- a/Open-ILS/src/eg2/src/app/share/print/print.component.ts +++ b/Open-ILS/src/eg2/src/app/share/print/print.component.ts @@ -18,12 +18,18 @@ export class PrintComponent implements OnInit { // Insertion point for externally-compiled templates htmlContainer: Element; + isPrinting: boolean; + + printQueue: PrintRequest[]; + constructor( private renderer: Renderer2, private elm: ElementRef, private store: StoreService, - private printer: PrintService - ) {} + private printer: PrintService) { + this.isPrinting = false; + this.printQueue = []; + } ngOnInit() { this.printer.onPrintRequest$.subscribe( @@ -34,10 +40,22 @@ export class PrintComponent implements OnInit { } handlePrintRequest(printReq: PrintRequest) { + + if (this.isPrinting) { + // Avoid print collisions by queuing requests as needed. + this.printQueue.push(printReq); + return; + } + + this.isPrinting = true; + this.applyTemplate(printReq); // Give templates a chance to render before printing - setTimeout(() => this.dispatchPrint(printReq)); + setTimeout(() => { + this.dispatchPrint(printReq) + this.reset(); + }); } applyTemplate(printReq: PrintRequest) { @@ -62,6 +80,18 @@ export class PrintComponent implements OnInit { } } + // Clear the print data + reset() { + this.isPrinting = false; + this.template = null; + this.context = null; + this.htmlContainer.innerHTML = ''; + + if (this.printQueue.length) { + this.handlePrintRequest(this.printQueue.pop()); + } + } + dispatchPrint(printReq: PrintRequest) { if (!printReq.text) { diff --git a/Open-ILS/src/eg2/src/app/staff/common.module.ts b/Open-ILS/src/eg2/src/app/staff/common.module.ts index 98de443fea..2dfbb3cd8f 100644 --- a/Open-ILS/src/eg2/src/app/staff/common.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/common.module.ts @@ -12,7 +12,6 @@ import {StringComponent} from '@eg/share/string/string.component'; import {StringService} from '@eg/share/string/string.service'; import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; import {DateSelectComponent} from '@eg/share/date-select/date-select.component'; -import {LegacyTemplateService} from '@eg/staff/share/legacy-template.service'; /** * Imports the EG common modules and adds modules common to all staff UI's. @@ -52,7 +51,6 @@ export class StaffCommonModule { return { ngModule: StaffCommonModule, providers: [ // Export staff-wide services - LegacyTemplateService, AccessKeyService, StringService, ToastService diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index 2f8cea43c8..dbea15aad7 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -74,10 +74,6 @@

- - -

- HELLO {{userContext.hello}} diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index 63bb2fa0de..7aaec92e1b 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -13,7 +13,6 @@ import {OrgService} from '@eg/core/org.service'; import {Pager} from '@eg/share/util/pager'; import {DateSelectComponent} from '@eg/share/date-select/date-select.component'; import {PrintService} from '@eg/share/print/print.service'; -import {LegacyTemplateService} from '@eg/staff/share//legacy-template.service'; @Component({ templateUrl: 'sandbox.component.html' @@ -54,8 +53,7 @@ export class SandboxComponent implements OnInit { private pcrud: PcrudService, private strings: StringService, private toast: ToastService, - private printer: PrintService, - private parser: LegacyTemplateService + private printer: PrintService ) {} ngOnInit() { @@ -91,6 +89,12 @@ export class SandboxComponent implements OnInit { contextData: {world : this.world}, printContext: 'default' }); + + this.printer.print({ + text: 'hello', + printContext: 'default' + }); + } changeDate(date) { @@ -126,68 +130,6 @@ export class SandboxComponent implements OnInit { .then(txt => this.toast.success(txt)); }, 4000); } - - testParser() { - const template = ` -
-
This item needs to be routed to - {{dest_location.shortname}} AKA {{dest_location.name}} -
-
i should disappear
-
i should also disappear
-
i should stick around
-
-
{{dest_address.street1}}
-
{{dest_address.street2}}
-
-
Slip Date: {{today | date:foo}}
-
Cost: {{cost | currency}}
-
    -
  1. -
    Barcode: {{copy.barcode}}
    -
    Title: {{copy.title | limitTo:10}}
    -
    - part = {{part}} -
    -
  2. -
-
-
THIS IS BR2
-
- THIS IS BR1 -
- swith bc = {{cp.barcode}} -
-
-
- `; - - const context = { - dest_location: { - shortname: 'BR1', - name: 'Branch Uno', - }, - dest_address: { - street1: '123 Pineapple Rd', - street2: 'APT #3', - }, - boolTrue: true, - boolFalse: false, - today: new Date(), - cost: 23.3, - copies: [ - {barcode: 'abc123', title: 'welcome to the jungle', parts: ['a', 'b', 'c']}, - {barcode: 'def456', title: 'hello mudda, hello fadda', parts: ['x', 'y']} - ] - }; - - this.parser.apply(template, context).then(html => { - this.printer.print({ - text: html, - printContext: 'default' - }); - }); - } } diff --git a/Open-ILS/src/eg2/src/app/staff/share/legacy-template.service.ts b/Open-ILS/src/eg2/src/app/staff/share/legacy-template.service.ts deleted file mode 100644 index fbd6265f5f..0000000000 --- a/Open-ILS/src/eg2/src/app/staff/share/legacy-template.service.ts +++ /dev/null @@ -1,295 +0,0 @@ -/** - * USING EVAL() OPENS A LARGE SECURITY HOLE. - * DEPRECATE ME. - * AngularJS-style minimal template parser. - * Original use case is supporting AngularJS style print templates. - * Template context data is applied only once at parsing time, there - * is no data binding. - * - * Supports the following template constructs: - * {{variables}} - * - * - * - * - */ -import {Injectable, EventEmitter} from '@angular/core'; -import {FormatService} from '@eg/share/util/format.service'; - -// Internal class for modeling a single template parsing instance. -class ParserInstance { - - private context: any; - private format: FormatService; - - // FormatService injected by ParserService - constructor(format: FormatService) { - this.format = format; - } - - // Given an HTML string and an interpolation context/scope, - // process the HTML template declarations, apply variable - // replacements, and return the restulting HTML string. - parse(html: string, context: any): string { - - // Shallow copy the context since we modify the keys internally - this.context = Object.assign({}, context); - - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - - // Parsing as html wraps the content in an wrapper - const domNode = doc.getElementsByTagName('body')[0]; - - this.traverse(domNode); - - return domNode.innerHTML; - } - - // Process each node in the in-progress document. - traverse(node: Node) { - if (!node) { return; } - - switch (node.nodeType) { - case Node.ELEMENT_NODE: - const complete = this.processElementNode(node as Element); - if (complete) { - return; - } - break; - - case Node.TEXT_NODE: - this.processTextNode(node as Text); - break; - } - - // Array.from() avoids TS compiler warnings - Array.from(node.childNodes).forEach(child => this.traverse(child)); - } - - // Process expressions found on each element. - // Returns true if the node was processed within and needs no - // further processing, false otherwise. - processElementNode(node: Element): boolean { - - const switchExp = node.getAttribute('ng-switch'); - if (switchExp) { - node.removeAttribute('ng-switch'); - if (!this.processSwitchExpression(node, switchExp)) { - // We removed all child nodes in the switch - return true; - } - } - - const ifExp = node.getAttribute('ng-if'); - if (ifExp) { - node.removeAttribute('ng-if'); - if (!this.testIfExpression(ifExp)) { - // A failing IF expression means the node does not render. - node.remove(); - return true; - } - } - - const loopExp = node.getAttribute('ng-repeat'); - if (loopExp) { - node.removeAttribute('ng-repeat'); - this.processLoopExpression(node, loopExp); - return true; - } - - return false; - } - - // Returns true if any of the switch expressions resulted - // in true, thus allowing a child node to remain and require - // future processing. - processSwitchExpression(node: Element, expr: string): boolean { - - // ng-switch only works on string values - const targetVal = this.getContextStringValue(expr); - let matchNode: Node; - - // Find the switch-matching child node - Array.from(node.childNodes).forEach(child => { - if (!matchNode && child.nodeType === Node.ELEMENT_NODE) { - const elm: Element = child as Element; - const val = elm.getAttribute('ng-switch-when'); - if (val === null || val === undefined) { return; } - if (val + '' === targetVal) { - matchNode = child; - } - } - }); - - // Remove all child nodes - while (node.firstChild) { - node.removeChild(node.firstChild); - } - - // Add the matching node back if found - if (matchNode) { - node.appendChild(matchNode); - return true; - } - - return false; // no matching switch values - } - - processLoopExpression(node: Element, expr: string) { - const parts = expr.split('in'); - const listItemKey = parts[0].trim(); - const listPath = parts[1].trim(); - const list = this.getContextValueAt(listPath); - - if (!Array.isArray(list)) { - throw new Error(`Template value ${listPath} is not an Array`); - } - - // Loop over the repeat array and for each iteration, add the - // loop variable as a new value in the (temporary) context so - // it can be referenced in the eval context. - const origContext = this.context; - let prevNode: Element = node; - - list.forEach(listItem => { - const listCtx = {}; - listCtx[listItemKey] = listItem; - this.context = Object.assign(this.context, listCtx); - const newNode = node.cloneNode(true) as Element; - // appendChild used internally when needed. - prevNode.parentNode.insertBefore(newNode, prevNode.nextSibling); - this.traverse(newNode); - prevNode = newNode; - }); - - // recover the original context sans any loop data. - this.context = origContext; - - // get rid of the source/template node - node.remove(); - } - - // To evaluate free-form expressions, create an environment - // where references to elements in the context are available. - // For example: ng-if="foo.bar" -- the 'foo' key from the - // context must be defined in advance for evaluation to succeed. - generateEvalEnv() { - let env = ''; - Object.keys(this.context).forEach(key => { - env += `var ${key} = this.context['${key}'];\n`; - }); - return env; - } - - // Returns true of the IF expression evaluates to true, - // false otherwise. - testIfExpression(expr: string): boolean { - - const env = this.generateEvalEnv(); - const evalStr = `${env}; Boolean(${expr})`; - // console.debug('ng-if eval string: ', evalStr); - - try { - return eval(evalStr); - } catch (err) { - // console.debug('IF expression failed with: ', err); - return false; - } - } - - // Replace variable {{...}} instances in a given Text node - // with the matching data from the context. - processTextNode(node: Text) { - if (!node || !node.data) { return; } - - const matches = node.data.match(/{{.*?}}/g); - if (!matches) { return; } - - matches.forEach(match => { - const dotpath = match.replace(/[{}]/g, ''); - node.replaceData( - node.data.indexOf(match), - match.length, - this.getContextStringValue(dotpath) - ); - }); - } - - // Returns the context item at the given path - getContextValueAt(dotpath: string): any { - let idx; - let obj = this.context; - const parts = dotpath.split('.'); - - for (idx = 0; idx < parts.length; idx++) { - obj = obj[parts[idx]]; - if (obj === null || typeof obj !== 'object') { - break; - } - } - - if (idx < parts.length - 1) { - // Loop exited before inspecting the whole path. - return null; - } - - return obj; - } - - // Find the value within the context at the given path. - getContextStringValue(dotpath: string): string { - - // Variable replacements may contain filters. - const pieces = dotpath.split('|').map(p => p.trim()); - const path = pieces[0]; - const filter = pieces[1]; - const data = { - datatype: null, // potentially applied below - value: this.getContextValueAt(path) - }; - - // TODO: teach the format service about processing due dates. - if (filter) { - const fParts = filter.split(':').map(p => p.trim()); - const fName = fParts[0]; - const fArgs = fParts.slice(1); - - switch (fName) { - case 'date': - data.datatype = 'timestamp'; - break; - case 'currency': - data.datatype = 'money'; - break; - case 'limitTo': - const size = fArgs[0]; - if (size) { - const offset = fArgs[1] || 0; - data.value = - data.value.substring(offset, offset + size); - } - break; - } - } - - return this.format.transform(data); - } -} - - -@Injectable() -export class LegacyTemplateService { - - constructor(private format: FormatService) {} - - // make async for now, may be needed later - public apply(html: string, context: any): Promise { - const parser = new ParserInstance(this.format); - - return new Promise((resolve, reject) => { - resolve(parser.parse(html, context)); - }); - } -} - -- 2.11.0