ad7e9ce180afcb38cb42fd7cb9753b0c092b7524
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / core / format.service.ts
1 import {Injectable} from '@angular/core';
2 import {DatePipe, CurrencyPipe} from '@angular/common';
3 import {IdlService, IdlObject} from '@eg/core/idl.service';
4 import {OrgService} from '@eg/core/org.service';
5
6 /**
7  * Format IDL vield values for display.
8  */
9
10 declare var OpenSRF;
11
12 export interface FormatParams {
13     value: any;
14     idlClass?: string;
15     idlField?: string;
16     datatype?: string;
17     orgField?: string; // 'shortname' || 'name'
18     datePlusTime?: boolean;
19 }
20
21 @Injectable({providedIn: 'root'})
22 export class FormatService {
23
24     dateFormat = 'shortDate';
25     dateTimeFormat = 'short';
26     wsOrgTimezone: string = OpenSRF.tz;
27
28     constructor(
29         private datePipe: DatePipe,
30         private currencyPipe: CurrencyPipe,
31         private idl: IdlService,
32         private org: OrgService
33     ) {
34
35         // Create an inilne polyfill for Number.isNaN, which is
36         // not available in PhantomJS for unit testing.
37         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
38         if (!Number.isNaN) {
39             // "The following works because NaN is the only value
40             // in javascript which is not equal to itself."
41             Number.isNaN = (value: any) => {
42                 return value !== value;
43             };
44         }
45     }
46
47     /**
48      * Create a human-friendly display version of any field type.
49      */
50     transform(params: FormatParams): string {
51         const value = params.value;
52
53         if (   value === undefined
54             || value === null
55             || value === ''
56             || Number.isNaN(value)) {
57             return '';
58         }
59
60         let datatype = params.datatype;
61
62         if (!datatype) {
63             if (params.idlClass && params.idlField) {
64                 datatype = this.idl.classes[params.idlClass]
65                     .field_map[params.idlField].datatype;
66             } else {
67                 // Assume it's a primitive value
68                 return value + '';
69             }
70         }
71
72         switch (datatype) {
73
74             case 'link':
75                 if (typeof value !== 'object') {
76                     return value + ''; // no fleshed value here
77                 }
78
79                 if (!params.idlClass || !params.idlField) {
80                     // Without a full accounting of the field data,
81                     // we can't determine the display value.
82                     return value + '';
83                 }
84
85                 const localClass = this.idl.classes[params.idlClass];
86
87                 if (!localClass) {
88                     console.warn(`No such IDL class ${params.idlClass}`);
89                     return value + '';
90                 }
91
92                 if (!localClass.field_map[params.idlField]) {
93                     console.warn(`IDL class ${params.idlClass} ` +
94                         `has no field named "${params.idlField}"`);
95                     return value + '';
96                 }
97
98                 const linkType = localClass.field_map[params.idlField]['reltype'];
99                 if (linkType !== 'has_a') {
100                     return value + ''; // eh?
101                 }
102
103                 const localField = localClass.field_map[params.idlField];
104                 const remoteKey = localField['key'];
105
106                 const remoteClass = this.idl.classes[localField['class']];
107                 const remoteField = remoteClass.field_map[remoteKey];
108                 const remoteSelector = remoteField.selector || remoteField.name;
109
110                 return value[remoteSelector]() + '';
111
112             case 'org_unit':
113                 const orgField = params.orgField || 'shortname';
114                 const org = this.org.get(value);
115                 return org ? org[orgField]() : '';
116
117             case 'timestamp':
118                 const date = new Date(value);
119                 let fmt = this.dateFormat || 'shortDate';
120                 if (params.datePlusTime) {
121                     fmt = this.dateTimeFormat || 'short';
122                 }
123                 return this.datePipe.transform(date, fmt);
124
125             case 'money':
126                 return this.currencyPipe.transform(value);
127
128             case 'bool':
129                 // Slightly better than a bare 't' or 'f'.
130                 // Should probably add a global true/false string.
131                 return Boolean(
132                     value === 't' || value === 1 ||
133                     value === '1' || value === true
134                 ).toString();
135
136             default:
137                 return value + '';
138         }
139     }
140 }
141