@esfaenza/extensions 15.2.3 → 15.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, NgModule, Injector, Injectable, Inject, Optional } from '@angular/core';
2
+ import { InjectionToken, Injectable, Optional, Inject, Pipe, NgModule, Injector } from '@angular/core';
3
+ import * as i1 from '@esfaenza/localizations';
4
+ import { LocalizationModule } from '@esfaenza/localizations';
5
+ import { first, map } from 'rxjs/operators';
6
+ import { Deserialize } from 'cerialize';
3
7
  import swal from 'sweetalert2';
4
8
  import { ToastrService } from 'ngx-toastr';
5
9
  import { of, Subject } from 'rxjs';
6
- import { map } from 'rxjs/operators';
7
- import { Deserialize } from 'cerialize';
8
10
 
9
- /**
10
- * Token che identifica il locale da utilizzare per questa libreria, sono supportati i locali 'it-IT' ed 'en-US'
11
- */
12
- const EXT_LOCALE = new InjectionToken('EXT_LOCALE');
13
- /**
14
- * Token che indica se la libreria deve utilizzare la libreria 'sweetalerts2' (più graficamente piacevolo) o se limitarsi ad usare 'toastr'
15
- */
16
- const EXT_FANCY_ALERTS = new InjectionToken('EXT_FANCY_ALERTS');
17
11
  /**
18
12
  * Token che indica il prefisso delle searchView che serve per storicizzarle evitando di tirarsi in memoria anche tutti gli oggetti. Se vuoto viene supposto 'Hot'
19
13
  */
@@ -25,843 +19,296 @@ const EXT_ALLOW_UTC = new InjectionToken('EXT_ALLOW_UTC');
25
19
 
26
20
  // Angular
27
21
  /**
28
- * Modulo di estensione che non registra nessun servizio di estensione, dovranno essere dichiarati 1 per 1 dall'applicazione che li utilizza
29
- */
30
- class ExtensionsModule {
31
- static forRoot(config) {
32
- return {
33
- ngModule: ExtensionsModule,
34
- providers: [
35
- { provide: EXT_LOCALE, useValue: config?.locale || 'it-IT' },
36
- { provide: EXT_FANCY_ALERTS, useValue: config?.fancy_alerts || true },
37
- { provide: APPSEARCH_PREFIX, useValue: config?.appsearch_prefix || 'Hot' },
38
- { provide: EXT_ALLOW_UTC, useValue: config?.allow_utc == null ? false : config?.allow_utc }
39
- ]
40
- };
41
- }
42
- }
43
- ExtensionsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
44
- ExtensionsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule });
45
- ExtensionsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule });
46
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, decorators: [{
47
- type: NgModule
48
- }] });
49
-
50
- // Angular
51
- /**
52
- * Service che fornisce funzionalità di alert nei tipi fondamentali: success, info, warning e danger
53
- */
54
- class MessageService {
22
+ * Service che fornisce dei metodi di estensione per la gestione/manipolazione delle date
23
+ */
24
+ class DateService {
55
25
  /**
56
26
  * Costruttore
57
27
  *
58
28
  * @ignore
59
29
  */
60
- constructor(injector, FANCY_ALERTS, LOCALE) {
61
- this.injector = injector;
62
- this.FANCY_ALERTS = FANCY_ALERTS;
63
- this.LOCALE = LOCALE;
30
+ constructor(locProvider, allowUtc) {
31
+ this.locProvider = locProvider;
32
+ this.allowUtc = allowUtc;
64
33
  /**
65
- * Oggetto statico in supporto alla localizzazione
34
+ * Oggetto statico di localizzazione per i messaggi di questa Service
66
35
  */
67
36
  this.loc = {
68
- 'Confirm': { 'en-US': "Confirm", 'it-IT': "Conferma", },
69
- 'Cancel': { 'en-US': "Cancel", 'it-IT': "Annulla", },
70
- 'Close': { 'en-US': "Close", 'it-IT': "Chiudi", },
71
- 'Error': { 'en-US': "Error", 'it-IT': "Errore", },
72
- 'Info': { 'en-US': "Info", 'it-IT': "Informazione", },
73
- 'Success': { 'en-US': "Success", 'it-IT': "Successo", },
74
- 'Warning': { 'en-US': "Warning", 'it-IT': "Attenzione", }
37
+ "SMALL_DATE_FORMAT": { 'en-US': "MM/DD/YYYY", 'it-IT': "DD/MM/YYYY" },
38
+ "FULL_DATE_FORMAT": { 'en-US': "MM/DD/YYYY HH:mm:ss", 'it-IT': "DD/MM/YYYY HH:mm:ss" },
39
+ "FULL_DATE_FORMAT_NO_SS": { 'en-US': "MM/DD/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" },
40
+ "SMALL_DATE_DISPLAY_FORMAT": { 'en-US': "DD/MM/YYYY", 'it-IT': "DD/MM/YYYY" },
41
+ "SMALL_DATE_DISPLAY_FORMAT_WITH_HOUR": { 'en-US': "DD/MM/YYYY HH", 'it-IT': "DD/MM/YYYY HH" },
42
+ "SMALL_DATE_DISPLAY_FORMAT_WITH_MINUTE": { 'en-US': "DD/MM/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" },
43
+ "FULL_DATE_DISPLAY_FORMAT": { 'en-US': "DD/MM/YYYY HH:mm:ss", 'it-IT': "DD/MM/YYYY HH:mm:ss" },
44
+ "FULL_DATE_DISPLAY_FORMAT_NO_SS": { 'en-US': "DD/MM/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" }
75
45
  };
76
- this.expiredSessionSwal = swal.mixin({
77
- showCancelButton: false,
78
- confirmButtonText: 'Login',
79
- customClass: {
80
- confirmButton: "btn btn-primary",
81
- container: "app-wrap",
82
- },
83
- buttonsStyling: false,
84
- reverseButtons: false,
85
- allowOutsideClick: false,
86
- icon: "warning"
87
- });
88
- this.successSwalWithCancel = swal.mixin({
89
- title: this.loc["Success"][this.LOCALE ?? 'it-IT'],
90
- customClass: {
91
- confirmButton: "btn btn-primary",
92
- cancelButton: "btn btn-secondary app-margin-right-10",
93
- container: "app-wrap",
94
- },
95
- buttonsStyling: false,
96
- reverseButtons: true,
97
- showCancelButton: true,
98
- confirmButtonText: this.loc["Confirm"][this.LOCALE ?? 'it-IT'],
99
- cancelButtonText: this.loc["Cancel"][this.LOCALE ?? 'it-IT'],
100
- icon: "success"
101
- });
102
- this.warningSwalWithCancel = swal.mixin({
103
- title: this.loc["Warning"][this.LOCALE ?? 'it-IT'],
104
- customClass: {
105
- confirmButton: "btn btn-primary",
106
- cancelButton: "btn btn-secondary app-margin-right-10",
107
- container: "app-wrap",
108
- },
109
- buttonsStyling: false,
110
- reverseButtons: true,
111
- showCancelButton: true,
112
- confirmButtonText: this.loc["Confirm"][this.LOCALE ?? 'it-IT'],
113
- cancelButtonText: this.loc["Cancel"][this.LOCALE ?? 'it-IT'],
114
- icon: "warning"
115
- });
116
- this.warningSwal = swal.mixin({
117
- title: this.loc["Warning"][this.LOCALE ?? 'it-IT'],
118
- customClass: {
119
- confirmButton: "btn btn-primary",
120
- container: "app-wrap",
121
- },
122
- buttonsStyling: false,
123
- reverseButtons: true,
124
- showCancelButton: false,
125
- confirmButtonText: this.loc["Close"][this.LOCALE ?? 'it-IT'],
126
- icon: "warning"
127
- });
128
- this.successSwal = swal.mixin({
129
- title: this.loc["Success"][this.LOCALE ?? 'it-IT'],
130
- customClass: {
131
- confirmButton: "btn btn-primary",
132
- container: "app-wrap",
133
- },
134
- buttonsStyling: false,
135
- reverseButtons: true,
136
- showCancelButton: false,
137
- confirmButtonText: this.loc["Close"][this.LOCALE ?? 'it-IT'],
138
- icon: "success"
139
- });
140
- this.errorSwal = swal.mixin({
141
- title: this.loc["Error"][this.LOCALE ?? 'it-IT'],
142
- customClass: {
143
- confirmButton: "btn btn-primary",
144
- container: "app-wrap",
145
- },
146
- buttonsStyling: false,
147
- reverseButtons: true,
148
- showCancelButton: false,
149
- confirmButtonText: this.loc["Close"][this.LOCALE ?? 'it-IT'],
150
- icon: "error"
151
- });
152
- this.infoSwal = swal.mixin({
153
- title: this.loc["Info"][this.LOCALE ?? 'it-IT'],
154
- customClass: {
155
- confirmButton: "btn btn-primary",
156
- container: "app-wrap",
157
- },
158
- buttonsStyling: false,
159
- reverseButtons: true,
160
- showCancelButton: false,
161
- confirmButtonText: this.loc["Close"][this.LOCALE ?? 'it-IT'],
162
- icon: "info"
163
- });
46
+ /**
47
+ * Oggetto di configurazione per tenere da conto le informazioni sui formati
48
+ */
49
+ this.cfg = {
50
+ fulldate: "",
51
+ fulldateDisplay: "",
52
+ smallDate: "",
53
+ fulldateNoSS: "",
54
+ smallDateWithH: "",
55
+ smallDateWithM: "",
56
+ fulldateDisplayNoSS: ""
57
+ };
58
+ this.init();
164
59
  }
165
- get toastr() {
166
- return this.injector.get(ToastrService);
60
+ async init() {
61
+ let LOCALE = this.locProvider ? await this.locProvider.Locale.pipe(first()).toPromise() : 'it-IT';
62
+ this.cfg.smallDate = this.loc["SMALL_DATE_FORMAT"][LOCALE];
63
+ this.cfg.fulldate = this.loc["FULL_DATE_FORMAT"][LOCALE];
64
+ this.cfg.fulldateNoSS = this.loc["FULL_DATE_FORMAT_NO_SS"][LOCALE];
65
+ this.cfg.fulldateDisplay = this.loc["FULL_DATE_DISPLAY_FORMAT"][LOCALE];
66
+ this.cfg.fulldateDisplayNoSS = this.loc["FULL_DATE_DISPLAY_FORMAT_NO_SS"][LOCALE];
67
+ this.cfg.smallDate = this.loc["SMALL_DATE_DISPLAY_FORMAT"][LOCALE];
68
+ this.cfg.smallDateWithH = this.loc["SMALL_DATE_DISPLAY_FORMAT_WITH_HOUR"][LOCALE];
69
+ this.cfg.smallDateWithM = this.loc["SMALL_DATE_DISPLAY_FORMAT_WITH_MINUTE"][LOCALE];
167
70
  }
168
71
  /**
169
- * Presentazione di un semplice messaggio di successo
72
+ * Aggiusta una data in "istante finale" in base a qual è la parte di data significativa.
170
73
  *
171
- * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
172
- * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
74
+ * Es. Istante finale delle ore: 05/10/1992 --> 04/10/1992 24:00(:00)
173
75
  *
174
- * @param {string} text Testo da mostrare
175
- * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
76
+ * @param {any} date Data qualsiasi (Date, stringa, dayjs...), verrà ricondotta ad un oggetto dayJs
77
+ * @param {'day' | 'hour' | 'minute'} timepart Parte significativa della data
78
+ * @param {boolean} printSeconds Indica se effettuare la stampa dei secondi o no
176
79
  */
177
- simpleSuccess(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
178
- let tos = this.toastrOrSwal(secondaryNotificationType);
179
- if (tos == "swal")
180
- this.successSwal.fire("", text);
181
- else
182
- this.toastr.success(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
80
+ adjustDateToFinalInstant(date, timepart, printSeconds = true) {
81
+ let mydt = this.getDateConvertion(date);
82
+ if (mydt == null)
83
+ return "FE: Not a Date";
84
+ let isZeroTime = mydt.startOf(timepart).valueOf() === mydt.valueOf();
85
+ if (!isZeroTime) {
86
+ if (timepart == "day" && mydt.subtract(1, "hour").hour() == mydt.hour()) {
87
+ // Se l'ora precedente è lo stesso orario dell'ora attuale l'istante finale dev'essere mandato avanti di 1
88
+ mydt = mydt.add(1, "hour");
89
+ }
90
+ return printSeconds ? mydt.format(this.cfg.fulldateDisplay) : mydt.format(this.cfg.fulldateDisplayNoSS);
91
+ }
92
+ let tmpValue = mydt.subtract(1, timepart);
93
+ let value = "";
94
+ // Istante finale delle ore: 05/10/1992 --> 04/10/1992 24:00(:00)
95
+ if (timepart == 'day')
96
+ value = tmpValue.format(this.cfg.smallDate) + " 24:00" + (printSeconds ? ':00' : '');
97
+ // Istante finale dei minuti: giorno 05/10/1992 04:00:00 --> 05/10/1992 03:60(:00)
98
+ else if (timepart == 'hour')
99
+ value = tmpValue.format(this.cfg.smallDateWithH) + ":60" + (printSeconds ? ':00' : '');
100
+ // Istante finale dei secondi: giorno 05/10/1992 04:10:00 --> 05/10/1992 04:09:60
101
+ else if (timepart == 'minute')
102
+ value = tmpValue.format(this.cfg.smallDateWithM) + ":60";
103
+ return value;
183
104
  }
184
105
  /**
185
- * Presentazione di un semplice messaggio di informazione
106
+ * Funzione che effettua l'ordinamento fra 2 date in base alla direzione specificata
186
107
  *
187
- * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
188
- * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
108
+ * @param {any} a Prima data per il confronto
109
+ * @param {any} b Seconda data per il confronto
110
+ * @param {'asc' | 'desc'} direction Direzione rispetto a cui ordinare le due date
189
111
  *
190
- * @param {string} text Testo da mostrare
191
- * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
112
+ * @returns {number} 0 se le date sono uguali, 1 se la data A deve venire logicamente prima della data B, -1 altrimenti
192
113
  */
193
- simpleInfo(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
194
- let tos = this.toastrOrSwal(secondaryNotificationType);
195
- if (tos == "swal")
196
- this.infoSwal.fire("", text);
197
- else
198
- this.toastr.info(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
114
+ dateSort(a, b, direction) {
115
+ if (a < b)
116
+ return direction == 'asc' ? 1 : -1;
117
+ else if (a > b)
118
+ return direction == 'asc' ? -1 : 1;
119
+ // con a uguale a b
120
+ return 0;
199
121
  }
200
122
  /**
201
- * Presentazione di un semplice messaggio di errore
123
+ * Effettua il trim dei secondi da una data espressa come stringa
202
124
  *
203
- * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
204
- * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
125
+ * @param {string} value Data espressa come stringa
205
126
  *
206
- * @param {string} text Testo da mostrare
207
- * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
127
+ * @returns {string} Data senza i secondi
208
128
  */
209
- simpleError(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
210
- let tos = this.toastrOrSwal(secondaryNotificationType);
211
- if (tos == "swal")
212
- this.errorSwal.fire("", text);
213
- else
214
- this.toastr.error(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
129
+ trimSeconds(value) {
130
+ //I numeri mangiati da dayjs spesso vengono ricondotti a date vere. Mi assicuro che se ci sono solo numeri proseguo
131
+ if (!isNaN(value))
132
+ return value;
133
+ //Se non ho qualcosa del tipo XX/YY/ZZZZ aa:bb:cc di sicuro non è una data che mi interessa
134
+ if (!/^\d\d\/\d\d\/\d\d\d\d \d\d:\d\d:\d\d$/g.test(value))
135
+ return value;
136
+ //In questo caso mi arrivano valori che devono essere stampati as-is, solo tagliando i secondi
137
+ return "'" + value.substr(0, value.length - 3);
215
138
  }
216
139
  /**
217
- * Presentazione di un semplice messaggio di avviso
140
+ * Data una lista di date Da e una lista di date A restituisce un oggetto rappresentante il range che include tutte le date passate
218
141
  *
219
- * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
220
- * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
142
+ * @param {any[]} fromDates Lista dei Da
143
+ * @param {any[]} toDates Lista dei A
221
144
  *
222
- * @param {string} text Testo da mostrare
223
- * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
145
+ * @returns {{ from: any, to: any }} Range di date Da - A che include tutti i valori di **fromDates** e **toDates**
224
146
  */
225
- simpleWarning(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
226
- let tos = this.toastrOrSwal(secondaryNotificationType);
227
- if (tos == "swal")
228
- this.warningSwal.fire("", text);
229
- else
230
- this.toastr.warning(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
147
+ getMinMaxDatesRange(fromDates, toDates) {
148
+ let datesFroms = [];
149
+ let datesTo = [];
150
+ for (let i = 0; i < fromDates.length; i++) {
151
+ let d = fromDates[i];
152
+ var convertion = this.getDateConvertion(d);
153
+ if (convertion)
154
+ datesFroms.push(convertion);
155
+ }
156
+ for (let i = 0; i < toDates.length; i++) {
157
+ let d = toDates[i];
158
+ var convertion = this.getDateConvertion(d);
159
+ if (convertion)
160
+ datesTo.push(convertion);
161
+ }
162
+ return { from: dayjs.min(datesFroms), to: dayjs.max(datesTo) };
231
163
  }
232
164
  /**
233
- * Ripulisce tutti i messaggi presenti in un dato momento sullo schermo
234
- */
235
- clearMessages() {
236
- this.toastr.clear();
237
- swal.close();
238
- }
239
- /**
240
- * Mostra un messaggio di avviso con richiesta di conferma
241
- *
242
- * @param {string} title Titolo del messaggio
243
- * @param {string} text Contenuto del messaggio
244
- *
245
- * @returns {Promise} Restituisce una Promise col risultato scelto dall'utente (conferma o no)
246
- */
247
- promiseWarningWithChoice(title, text) {
248
- return this.warningSwalWithCancel.fire(title, text);
249
- }
250
- /**
251
- * In base alla configurazione attuale e all'eventuale override per la chiamata del caso indica se bisogna utilizzare toastr o sweetalert
252
- *
253
- * @param {"toastr" | "swal" | null} providerOverride Override alla modalità configurata
254
- *
255
- * @returns {"toastr" | "swal"} Indicazione su che provider di messaggi di avviso debba essere utilizzato
256
- */
257
- toastrOrSwal(providerOverride) {
258
- if (providerOverride != null)
259
- return providerOverride;
260
- return this.FANCY_ALERTS == null ? "swal" : this.FANCY_ALERTS ? "swal" : "toastr";
261
- }
262
- /**
263
- * Mostra un messaggio di avviso con richiesta di conferma e gestisce il risultato chiamato i callback **onsuccess** o **onerror**
165
+ * Ottiene la conversione di una data in qualsiasi formato al formato standard DayJs
264
166
  *
265
- * @param {string} title Titolo del messaggio
266
- * @param {string} text Contenuto del messaggio
267
- * @param {Function} onsuccess Callback da chimare su conferma
268
- * @param {Function} onerror Callback da chimare su errori
269
- */
270
- simpleWarningWithChoice(title, text, onsuccess = null, onerror = null) {
271
- this.warningSwalWithCancel.fire(title, text).then((result) => {
272
- if (result.value && onsuccess)
273
- onsuccess();
274
- else if (onerror)
275
- onerror();
276
- });
277
- }
278
- /**
279
- * Mostra un messaggio di success con richiesta di conferma e gestisce il risultato chiamato i callback **onconfirm** o **onabort**
167
+ * @param {any} date Oggetto rappresentante una data. Potrebbe essere una stringa, un Date o già un DayJs
168
+ * @param {boolean} useUtc Indica se usare l'estensione utc di Dayjs per parsare la data o no
280
169
  *
281
- * @param {string} title Titolo del messaggio
282
- * @param {string} text Contenuto del messaggio
283
- * @param {Function} onconfirm Callback da chimare su conferma
284
- * @param {Function} onabort Callback da chimare su annullamento
285
- * @param {string} confirmtext Testo del pulsante di azione
286
- * @param {string} aborttext Testo del pulsante di annullamento
170
+ * @returns {any} Data in modalità DayJs
287
171
  */
288
- simpleSuccessWithChoice(title, text, onconfirm = null, onabort = null, confirmtext = "", aborttext = '') {
289
- var configuration = {
290
- title: title,
291
- html: text
292
- };
293
- if (confirmtext)
294
- configuration["confirmButtonText"] = confirmtext;
295
- if (aborttext)
296
- configuration["cancelButtonText"] = aborttext;
297
- this.successSwalWithCancel.fire(configuration).then((result) => {
298
- if (result.value && onconfirm)
299
- onconfirm();
300
- else if (onabort)
301
- onabort();
302
- });
172
+ getDateConvertion(date, useUtc = false) {
173
+ if (!date)
174
+ return null;
175
+ if (this.isDayJs(date))
176
+ return date;
177
+ if (this.isJsDate(date))
178
+ return useUtc ? dayjs.utc(date) : dayjs(date);
179
+ // Se non c'è la proprietà length vuol dire che non è una stringa e a questo punto non ho idea di che minchia sia
180
+ let length = date.length;
181
+ if (!length)
182
+ return null;
183
+ if (useUtc && !this.allowUtc)
184
+ throw "@esfaenza/extensions: Richiesta data UTC ma da configurazione la libreria non lo supporta";
185
+ // Per risparmiare chiamate a tentoni, in base alla lunghezza della stringa chiamo direttamente il metodo giusto,
186
+ // controllando poi che mi generi una data come me la aspetto io
187
+ let tryThis = null;
188
+ let toCall = useUtc ? dayjs.utc : dayjs;
189
+ // Se la data contiene la lettera "T" vuol dire che è una data del tipo 2020-10-15T00:00:00, direttamente parsabile dal dayjs col locale corretto.
190
+ // Per il resto mi baso sulla lunghezza della stringa
191
+ if (date.includes("T"))
192
+ tryThis = toCall(date);
193
+ else if (length == this.cfg.fulldate.length)
194
+ tryThis = toCall(date, this.cfg.fulldate);
195
+ else if (length == this.cfg.smallDate.length)
196
+ tryThis = toCall(date, this.cfg.smallDate);
197
+ else if (length == this.cfg.fulldateNoSS.length)
198
+ tryThis = toCall(date, this.cfg.fulldateNoSS);
199
+ if (tryThis && tryThis.isValid && tryThis.isValid())
200
+ return tryThis;
201
+ // Se niente funziona, null.
202
+ // Ricondurre l'Input ad una data non è possibile.
203
+ return null;
303
204
  }
304
205
  /**
305
- * Mostra un messaggio di avviso con richiesta di conferma e gestisce il risultato chiamato i callback **onsuccess** o **onerror**
206
+ * Helper che restituisce **true** qualora l'oggetto passato fosse una data vera e propria, **false** altrimenti
306
207
  *
307
- * @param {string} title Titolo del messaggio
308
- * @param {string} text Contenuto del messaggio
309
- */
310
- observableSimpleWarningWithChoice(title, text) {
311
- return of(this.warningSwalWithCancel.fire(title, text)).pipe(map(t => !!t.value));
312
- }
313
- /**
314
- * Mostra un messaggio di avviso che la sessione corrente è scaduta e propone all'utente di navigare al login. Qualora l'utente decidesse di farlo
315
- * verrà chiamata la funzione di callback **onnavigate
208
+ * @param {any} check Oggetto da controllare
316
209
  *
317
- * @param {string} title Titolo del messaggio
318
- * @param {string} text Contenuto del messaggio
319
- * @param {Function} onnavigate Callback da chimare su navigazione in corso
210
+ * @returns {boolean} Indicazione se l'oggetto è un vero Date di Javascript o no
320
211
  */
321
- expiredSessionMessage(title, text, onnavigate = null) {
322
- this.expiredSessionSwal.fire(title, text).then((result) => {
323
- if (result.value && onnavigate)
324
- onnavigate();
325
- });
212
+ isJsDate(check) {
213
+ return check && Object.prototype.toString.call(check) === "[object Date]" && !isNaN(check);
326
214
  }
327
215
  /**
328
- * Metodo helper per gestire in maniera standardizzata una risposta di tipo **CallResult**
216
+ * Helper che restituisce **true** qualora l'oggetto passato fosse un DayJs vero e proprio, **false** altrimenti
329
217
  *
330
- * @param {CallResult} response Risposta ricevuta dal server
331
- * @param {string} successMsg Messaggio da visualizzare in caso di successo
332
- * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
333
- * @param {Function} postSuccess Callback da chimare in caso di successo
334
- * @param {Function} postFailure Callback da chimare in caso di fallimento
218
+ * @param {any} check Oggetto da controllare
219
+ * @returns {boolean} Indicazione se l'oggetto è un vero DayJs o no
335
220
  */
336
- manageCallResultResponse(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
337
- if (response.success) {
338
- if (response.haswarning)
339
- this.simpleWarning(response.warning);
340
- else if (successMsg)
341
- this.simpleSuccess(successMsg);
342
- if (postSuccess)
343
- postSuccess();
344
- }
345
- else {
346
- this.simpleError((errorMsg ? errorMsg + ": " : "") + response.haserror ? response.error : '[NO ERR.]');
347
- if (postFailure)
348
- postFailure();
349
- }
221
+ isDayJs(check) {
222
+ // Non uso instanceof perché sarebbe estremamente più lento
223
+ // Non uso nemmeno isDayjs() della libreria Dayjs dato che ha lo stesso problema: https://github.com/iamkun/dayjs/blob/06f88f425828b1ce96b737332d25145a95a4ee9d/src/index.js#L9
224
+ return check.$D !== undefined && check.$M !== undefined && check.$y !== undefined;
350
225
  }
351
226
  /**
352
- * Metodo helper per gestire in maniera standardizzata una risposta di tipo **string**, considerando che:
353
- *
354
- * Stringa vuota --> Tutto bene
355
- *
356
- * Stringa piena --> Questo è il messaggio d'errore per cui la chiamata è fallita
227
+ * Ottiene la formattazione di una data in base alla versione richiesta "small" o "long" e a se stampare o meno i secondi
357
228
  *
358
- * @param {string} response Risposta ricevuta dal server
359
- * @param {string} successMsg Messaggio da visualizzare in caso di successo
360
- * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
361
- * @param {Function} postSuccess Callback da chimare in caso di successo
362
- * @param {Function} postFailure Callback da chimare in caso di fallimento
363
- */
364
- manageStringResponse(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
365
- if (!response) {
366
- if (successMsg)
367
- this.simpleSuccess(successMsg);
368
- if (postSuccess)
369
- postSuccess();
370
- }
371
- else {
372
- if (errorMsg)
373
- this.simpleError(errorMsg + ": " + response);
374
- if (postFailure)
375
- postFailure();
376
- }
377
- }
378
- /**
379
- * Metodo helper per gestire in maniera standardizzata una risposta di tipo **boolean**
229
+ * @param {any} date Oggetto rappresentante una data. Potrebbe essere una stringa, un Date o già un DayJs
230
+ * @param {boolean} small Indica se formattarla con un formato breve (se **true**) o un formato più lungo (se **false**)
380
231
  *
381
- * @param {string} response Risposta ricevuta dal server
382
- * @param {string} successMsg Messaggio da visualizzare in caso di successo
383
- * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
384
- * @param {Function} postSuccess Callback da chimare in caso di successo
385
- * @param {Function} postFailure Callback da chimare in caso di fallimento
232
+ * @returns {string} Data formattata nel formato richiesto
386
233
  */
387
- manageStringResponseBool(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
388
- if (response) {
389
- if (successMsg)
390
- this.simpleSuccess(successMsg);
391
- if (postSuccess)
392
- postSuccess();
393
- }
394
- else {
395
- if (errorMsg)
396
- this.simpleError(errorMsg);
397
- if (postFailure)
398
- postFailure();
399
- }
234
+ getFormatted(date, small, seconds) {
235
+ var date = this.getDateConvertion(date);
236
+ if (!date)
237
+ return null;
238
+ return date.format(small ? this.cfg.smallDate : seconds ? this.cfg.fulldateDisplay : this.cfg.fulldateDisplayNoSS);
400
239
  }
401
240
  }
402
- MessageService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService, deps: [{ token: Injector }, { token: EXT_FANCY_ALERTS, optional: true }, { token: EXT_LOCALE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
403
- MessageService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService });
404
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService, decorators: [{
405
- type: Injectable
406
- }], ctorParameters: function () { return [{ type: i0.Injector, decorators: [{
407
- type: Inject,
408
- args: [Injector]
409
- }] }, { type: undefined, decorators: [{
241
+ DateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService, deps: [{ token: i1.BaseLocalization, optional: true }, { token: EXT_ALLOW_UTC, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
242
+ DateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService, providedIn: "root" });
243
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService, decorators: [{
244
+ type: Injectable,
245
+ args: [{ providedIn: "root" }]
246
+ }], ctorParameters: function () { return [{ type: i1.BaseLocalization, decorators: [{
410
247
  type: Optional
411
- }, {
412
- type: Inject,
413
- args: [EXT_FANCY_ALERTS]
414
248
  }] }, { type: undefined, decorators: [{
415
249
  type: Optional
416
250
  }, {
417
251
  type: Inject,
418
- args: [EXT_LOCALE]
252
+ args: [EXT_ALLOW_UTC]
419
253
  }] }]; } });
420
254
 
421
255
  // Angular
422
256
  /**
423
- * Service che fornisce un accesso base in lettura/scrittura sulla clipboard
257
+ * Pipe che permette di formattare una data in base al formato specificato. Utilizza il **DateService** di **@esfaenza/extensions** per ricondurre l'input a una data valida
424
258
  */
425
- class ClipboardService {
259
+ class LocDatePipe {
426
260
  /**
427
- * Costruttore
428
- *
429
261
  * @ignore
430
262
  */
431
- constructor(msgService, LOCALE) {
432
- this.msgService = msgService;
433
- this.LOCALE = LOCALE;
434
- /**
435
- * Oggetto statico di localizzazione per i messaggi di questa Service
436
- */
437
- this.loc = {
438
- "Unfortunately Firefox doesn't yet support direct Clipboard access if not via extensions, Please use Chrome to do this operation": {
439
- 'en-US': "Unfortunately Firefox doesn't yet support direct Clipboard access if not via extensions, Please use Chrome to do this operation",
440
- 'it-IT': "Mi dispiace ma Firefox non supporta l'accesso diretto alla Clipboard, se non attraverso le estensioni. Si prega di usare Chrome"
441
- },
442
- "Generic error during 'copy' command execution": {
443
- 'en-US': "Generic error during 'copy' command execution",
444
- 'it-IT': "Errore generico durante l'esecuzione del comando 'copy'"
445
- },
446
- "Error during Clipboard content read operation": {
447
- 'en-US': "Error during Clipboard content read operation",
448
- 'it-IT': "Errore nella lettura del contenuto della Clipboard"
449
- },
450
- "Error during Clipboard content copy operation": {
451
- 'en-US': "Error during Clipboard content copy operation",
452
- 'it-IT': "Errore nella scrittura del contenuto nella Clipboard"
453
- },
454
- "Please insert your clipboard content": {
455
- 'en-US': "Please insert your clipboard content",
456
- 'it-IT': "Si prega di incollare il contenuto della Clipboard"
457
- },
458
- "User input required": {
459
- 'en-US': "User input required",
460
- 'it-IT': "Richiesto Input utente"
461
- },
462
- "Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation": {
463
- 'en-US': "Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation",
464
- 'it-IT': "Il browser corrente non supporta la lettura diretta dei dati della Clipboard, si prega di effettuare un'incolla (CTRL + V) nella casella di testo e confermare l'operazione"
465
- },
466
- "Confirm": {
467
- 'en-US': "Confirm",
468
- 'it-IT': "Conferma"
469
- },
470
- "Cancel": {
471
- 'en-US': "Cancel",
472
- 'it-IT': "Annulla"
473
- },
474
- };
475
- }
476
- /**
477
- * Prende il valore della clipboard.
478
- * Si può specificare se ci si aspetta una lista o una matrice di valori in modo da ricevere già un risultato utilizzabile
479
- *
480
- * @param {'array' | 'matrix'} style Stile dell'output desiderato, se array o matrice
481
- * @param {Function} valueFoundCallback Callback che questa funzione chiamerà una volta ottenuto i valori richiesti dalla clipboard
482
- */
483
- getClipboard(style, valueFoundCallback) {
484
- if (navigator.userAgent.indexOf("Firefox") != -1) {
485
- swal.fire({
486
- title: this.loc["User input required"][this.LOCALE ?? 'it-IT'],
487
- text: this.loc["Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation"][this.LOCALE ?? 'it-IT'],
488
- input: 'textarea',
489
- icon: "warning",
490
- confirmButtonText: this.loc["Confirm"][this.LOCALE ?? 'it-IT'],
491
- cancelButtonText: this.loc["Cancel"][this.LOCALE ?? 'it-IT'],
492
- showCancelButton: true,
493
- customClass: {
494
- confirmButton: "btn btn-primary",
495
- cancelButton: "btn btn-secondary app-margin-right-15",
496
- container: "app-wrap",
497
- },
498
- reverseButtons: true,
499
- allowOutsideClick: false,
500
- inputValidator: (value) => {
501
- if (!value)
502
- return this.loc["Please insert your clipboard content"][this.LOCALE ?? 'it-IT'];
503
- }
504
- }).then(t => {
505
- if (t.isConfirmed) {
506
- console.log("[@esfaenza/extensions] Retrieving input data from user input");
507
- this.doClipboardDataGet(t.value, style, valueFoundCallback);
508
- }
509
- });
510
- return;
511
- }
512
- if (window.clipboardData) {
513
- console.log("[@esfaenza/extensions] Found clipboardData");
514
- let data = window.clipboardData.getData("Text");
515
- this.doClipboardDataGet(data, style, valueFoundCallback);
516
- }
517
- else if (window.navigator.clipboard) {
518
- console.log("[@esfaenza/extensions] Found navigator clipboard");
519
- window.navigator.permissions.query({ name: 'clipboard-read' }).then(result => {
520
- if (!(result.state === 'granted' || result.state === 'prompt')) {
521
- console.error("[@esfaenza/extensions] Clipboard permission not granted");
522
- if (valueFoundCallback)
523
- valueFoundCallback([]);
524
- return;
525
- }
526
- console.log("[@esfaenza/extensions] Clipboard permission granted, retrieving values");
527
- window.navigator.clipboard.readText()
528
- .then((t) => this.doClipboardDataGet(t, style, valueFoundCallback))
529
- .catch((err) => this.msgService.simpleError(this.loc["Error during Clipboard content read operation"][this.LOCALE ?? 'it-IT'] + ": " + err));
530
- });
531
- }
532
- }
533
- /** @ignore */
534
- doClipboardDataGet(clipboardText, style, valueFoundCallback) {
535
- console.log("[@esfaenza/extensions] Retrieved clipboard values: " + clipboardText);
536
- let value = style == "array" ? this.getClipboardLines(clipboardText) :
537
- style == "matrix" ? this.getClipboardMatrix(clipboardText) : [];
538
- console.log(`[@esfaenza/extensions] Adapted for ${style}: ${JSON.stringify(value)}`);
539
- if (valueFoundCallback)
540
- valueFoundCallback(value);
541
- }
542
- /**
543
- * Ottiene il contenuto della clipboard diviso per righe (separate da newline)
544
- *
545
- * @param {string} data contenuto letto dalla clipboard
546
- *
547
- * @returns {string[]} Array contenente il testo preso dalla clipboard diviso per riga
548
- */
549
- getClipboardLines(data) {
550
- let trimmedData = data.trim();
551
- if (!trimmedData.includes("\r\n") && trimmedData.includes("\n"))
552
- return trimmedData.split("\n");
553
- return trimmedData.split("\r\n");
263
+ constructor(dts) {
264
+ this.dts = dts;
554
265
  }
555
266
  /**
556
- * Ottiene il contenuto della clipboard diviso per righe (separate da newline) e colonne (separate da tab)
267
+ * Trasformazione della data in input in base al formato richiesto
557
268
  *
558
- * @param {string} data contenuto letto dalla clipboard
559
- *
560
- * @returns {string[][]} Matrice contenente il testo preso dalla clipboard diviso per riga e colonna
561
- */
562
- getClipboardMatrix(data) {
563
- var ret = [];
564
- var rows = !data.includes("\r\n") && data.includes("\n") ? data.split("\n") : data.split("\r\n");
565
- // A quanto pare ogni valore è separato da un \t... spero che sia uno standard di quando si copia da excel altrimenti ciao
566
- for (let i = 0; i < rows.length; i++)
567
- ret.push(rows[i].split('\t').map(t => t.trim()));
568
- return ret;
569
- }
570
- /**
571
- * Metodo che copia un testo all'interno della clipboard
269
+ * @param {any} value Valore da ricondurre ad una data e formattare
270
+ * @param {string} format Formato in output della data da visualizzare
572
271
  *
573
- * @param {string} text Testo da inserire nella clipboard
272
+ * @returns {string} Data formattata in base al formato
574
273
  */
575
- copyTextToClipboard(text) {
576
- var textArea = document.createElement("textarea");
577
- //
578
- // *** This styling is an extra step which is likely not required. ***
579
- //
580
- // Why is it here? To ensure:
581
- // 1. the element is able to have focus and selection.
582
- // 2. if element was to flash render it has minimal visual impact.
583
- // 3. less flakyness with selection and copying which **might** occur if
584
- // the textarea element is not visible.
585
- //
586
- // The likelihood is the element won't even render, not even a flash,
587
- // so some of these are just precautions. However in IE the element
588
- // is visible whilst the popup box asking the user for permission for
589
- // the web page to copy to the clipboard.
590
- //
591
- // Place in top-left corner of screen regardless of scroll position.
592
- textArea.style.position = "fixed";
593
- textArea.style.top = "0";
594
- textArea.style.left = "0";
595
- // Ensure it has a small width and height. Setting to 1px / 1em
596
- // doesn't work as this gives a negative w/h on some browsers.
597
- textArea.style.width = "2em";
598
- textArea.style.height = "2em";
599
- // We don't need padding, reducing the size if it does flash render.
600
- textArea.style.padding = "0";
601
- // Clean up any borders.
602
- textArea.style.border = "none";
603
- textArea.style.outline = "none";
604
- textArea.style.boxShadow = "none";
605
- // Avoid flash of white box if rendered for any reason.
606
- textArea.style.background = "transparent";
607
- textArea.value = text;
608
- document.body.appendChild(textArea);
609
- textArea.focus();
610
- textArea.select();
611
- try {
612
- var successful = document.execCommand("copy");
613
- if (!successful)
614
- this.msgService.simpleError(this.loc["Generic error during 'copy' command execution"][this.LOCALE ?? 'it-IT']);
615
- }
616
- catch (err) {
617
- this.msgService.simpleError(this.loc["Error during Clipboard content copy operation"][this.LOCALE ?? 'it-IT'] + ": " + err);
618
- }
619
- document.body.removeChild(textArea);
274
+ transform(value, format = "DD/MM/YYYY") {
275
+ if (value == null || value === "")
276
+ return null;
277
+ var detatchedValue = JSON.parse(JSON.stringify(value));
278
+ var dt = this.dts.getDateConvertion(detatchedValue);
279
+ return dt.format(format);
620
280
  }
621
281
  }
622
- ClipboardService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService, deps: [{ token: MessageService }, { token: EXT_LOCALE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
623
- ClipboardServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService });
624
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService, decorators: [{
625
- type: Injectable
626
- }], ctorParameters: function () { return [{ type: MessageService }, { type: undefined, decorators: [{
627
- type: Optional
628
- }, {
629
- type: Inject,
630
- args: [EXT_LOCALE]
631
- }] }]; } });
282
+ LocDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LocDatePipe, deps: [{ token: DateService }], target: i0.ɵɵFactoryTarget.Pipe });
283
+ LocDatePipepipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: LocDatePipe, name: "loc_date" });
284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LocDatePipe, decorators: [{
285
+ type: Pipe,
286
+ args: [{ name: "loc_date", pure: true }]
287
+ }], ctorParameters: function () { return [{ type: DateService }]; } });
632
288
 
633
289
  // Angular
634
- /**
635
- * Service che fornisce dei metodi di estensione per la gestione/manipolazione delle date
636
- */
637
- class DateService {
638
- /**
639
- * Costruttore
640
- *
641
- * @ignore
642
- */
643
- constructor(LOCALE, allowUtc) {
644
- this.LOCALE = LOCALE;
645
- this.allowUtc = allowUtc;
646
- /**
647
- * Oggetto statico di localizzazione per i messaggi di questa Service
648
- */
649
- this.loc = {
650
- "SMALL_DATE_FORMAT": { 'en-US': "MM/DD/YYYY", 'it-IT': "DD/MM/YYYY" },
651
- "FULL_DATE_FORMAT": { 'en-US': "MM/DD/YYYY HH:mm:ss", 'it-IT': "DD/MM/YYYY HH:mm:ss" },
652
- "FULL_DATE_FORMAT_NO_SS": { 'en-US': "MM/DD/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" },
653
- "SMALL_DATE_DISPLAY_FORMAT": { 'en-US': "DD/MM/YYYY", 'it-IT': "DD/MM/YYYY" },
654
- "SMALL_DATE_DISPLAY_FORMAT_WITH_HOUR": { 'en-US': "DD/MM/YYYY HH", 'it-IT': "DD/MM/YYYY HH" },
655
- "SMALL_DATE_DISPLAY_FORMAT_WITH_MINUTE": { 'en-US': "DD/MM/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" },
656
- "FULL_DATE_DISPLAY_FORMAT": { 'en-US': "DD/MM/YYYY HH:mm:ss", 'it-IT': "DD/MM/YYYY HH:mm:ss" },
657
- "FULL_DATE_DISPLAY_FORMAT_NO_SS": { 'en-US': "DD/MM/YYYY HH:mm", 'it-IT': "DD/MM/YYYY HH:mm" }
658
- };
659
- /**
660
- * Oggetto di configurazione per tenere da conto le informazioni sui formati
661
- */
662
- this.cfg = {
663
- fulldate: "",
664
- fulldateDisplay: "",
665
- smallDate: "",
666
- fulldateNoSS: "",
667
- smallDateWithH: "",
668
- smallDateWithM: "",
669
- fulldateDisplayNoSS: ""
290
+ class ExtensionsModule {
291
+ static forRoot(config) {
292
+ return {
293
+ ngModule: ExtensionsModule,
294
+ providers: [
295
+ { provide: APPSEARCH_PREFIX, useValue: config?.appsearch_prefix || 'Hot' },
296
+ { provide: EXT_ALLOW_UTC, useValue: config?.allow_utc == null ? false : config?.allow_utc }
297
+ ]
670
298
  };
671
- this.cfg.smallDate = this.loc["SMALL_DATE_FORMAT"][this.LOCALE ?? 'it-IT'];
672
- this.cfg.fulldate = this.loc["FULL_DATE_FORMAT"][this.LOCALE ?? 'it-IT'];
673
- this.cfg.fulldateNoSS = this.loc["FULL_DATE_FORMAT_NO_SS"][this.LOCALE ?? 'it-IT'];
674
- this.cfg.fulldateDisplay = this.loc["FULL_DATE_DISPLAY_FORMAT"][this.LOCALE ?? 'it-IT'];
675
- this.cfg.fulldateDisplayNoSS = this.loc["FULL_DATE_DISPLAY_FORMAT_NO_SS"][this.LOCALE ?? 'it-IT'];
676
- this.cfg.smallDate = this.loc["SMALL_DATE_DISPLAY_FORMAT"][this.LOCALE ?? 'it-IT'];
677
- this.cfg.smallDateWithH = this.loc["SMALL_DATE_DISPLAY_FORMAT_WITH_HOUR"][this.LOCALE ?? 'it-IT'];
678
- this.cfg.smallDateWithM = this.loc["SMALL_DATE_DISPLAY_FORMAT_WITH_MINUTE"][this.LOCALE ?? 'it-IT'];
679
- }
680
- /**
681
- * Aggiusta una data in "istante finale" in base a qual è la parte di data significativa.
682
- *
683
- * Es. Istante finale delle ore: 05/10/1992 --> 04/10/1992 24:00(:00)
684
- *
685
- * @param {any} date Data qualsiasi (Date, stringa, dayjs...), verrà ricondotta ad un oggetto dayJs
686
- * @param {'day' | 'hour' | 'minute'} timepart Parte significativa della data
687
- * @param {boolean} printSeconds Indica se effettuare la stampa dei secondi o no
688
- */
689
- adjustDateToFinalInstant(date, timepart, printSeconds = true) {
690
- let mydt = this.getDateConvertion(date);
691
- if (mydt == null)
692
- return "FE: Not a Date";
693
- let isZeroTime = mydt.startOf(timepart).valueOf() === mydt.valueOf();
694
- if (!isZeroTime) {
695
- if (timepart == "day" && mydt.subtract(1, "hour").hour() == mydt.hour()) {
696
- // Se l'ora precedente è lo stesso orario dell'ora attuale l'istante finale dev'essere mandato avanti di 1
697
- mydt = mydt.add(1, "hour");
698
- }
699
- return printSeconds ? mydt.format(this.cfg.fulldateDisplay) : mydt.format(this.cfg.fulldateDisplayNoSS);
700
- }
701
- let tmpValue = mydt.subtract(1, timepart);
702
- let value = "";
703
- // Istante finale delle ore: 05/10/1992 --> 04/10/1992 24:00(:00)
704
- if (timepart == 'day')
705
- value = tmpValue.format(this.cfg.smallDate) + " 24:00" + (printSeconds ? ':00' : '');
706
- // Istante finale dei minuti: giorno 05/10/1992 04:00:00 --> 05/10/1992 03:60(:00)
707
- else if (timepart == 'hour')
708
- value = tmpValue.format(this.cfg.smallDateWithH) + ":60" + (printSeconds ? ':00' : '');
709
- // Istante finale dei secondi: giorno 05/10/1992 04:10:00 --> 05/10/1992 04:09:60
710
- else if (timepart == 'minute')
711
- value = tmpValue.format(this.cfg.smallDateWithM) + ":60";
712
- return value;
713
- }
714
- /**
715
- * Funzione che effettua l'ordinamento fra 2 date in base alla direzione specificata
716
- *
717
- * @param {any} a Prima data per il confronto
718
- * @param {any} b Seconda data per il confronto
719
- * @param {'asc' | 'desc'} direction Direzione rispetto a cui ordinare le due date
720
- *
721
- * @returns {number} 0 se le date sono uguali, 1 se la data A deve venire logicamente prima della data B, -1 altrimenti
722
- */
723
- dateSort(a, b, direction) {
724
- if (a < b)
725
- return direction == 'asc' ? 1 : -1;
726
- else if (a > b)
727
- return direction == 'asc' ? -1 : 1;
728
- // con a uguale a b
729
- return 0;
730
- }
731
- /**
732
- * Effettua il trim dei secondi da una data espressa come stringa
733
- *
734
- * @param {string} value Data espressa come stringa
735
- *
736
- * @returns {string} Data senza i secondi
737
- */
738
- trimSeconds(value) {
739
- //I numeri mangiati da dayjs spesso vengono ricondotti a date vere. Mi assicuro che se ci sono solo numeri proseguo
740
- if (!isNaN(value))
741
- return value;
742
- //Se non ho qualcosa del tipo XX/YY/ZZZZ aa:bb:cc di sicuro non è una data che mi interessa
743
- if (!/^\d\d\/\d\d\/\d\d\d\d \d\d:\d\d:\d\d$/g.test(value))
744
- return value;
745
- //In questo caso mi arrivano valori che devono essere stampati as-is, solo tagliando i secondi
746
- return "'" + value.substr(0, value.length - 3);
747
- }
748
- /**
749
- * Data una lista di date Da e una lista di date A restituisce un oggetto rappresentante il range che include tutte le date passate
750
- *
751
- * @param {any[]} fromDates Lista dei Da
752
- * @param {any[]} toDates Lista dei A
753
- *
754
- * @returns {{ from: any, to: any }} Range di date Da - A che include tutti i valori di **fromDates** e **toDates**
755
- */
756
- getMinMaxDatesRange(fromDates, toDates) {
757
- let datesFroms = [];
758
- let datesTo = [];
759
- for (let i = 0; i < fromDates.length; i++) {
760
- let d = fromDates[i];
761
- var convertion = this.getDateConvertion(d);
762
- if (convertion)
763
- datesFroms.push(convertion);
764
- }
765
- for (let i = 0; i < toDates.length; i++) {
766
- let d = toDates[i];
767
- var convertion = this.getDateConvertion(d);
768
- if (convertion)
769
- datesTo.push(convertion);
770
- }
771
- return { from: dayjs.min(datesFroms), to: dayjs.max(datesTo) };
772
- }
773
- /**
774
- * Ottiene la conversione di una data in qualsiasi formato al formato standard DayJs
775
- *
776
- * @param {any} date Oggetto rappresentante una data. Potrebbe essere una stringa, un Date o già un DayJs
777
- * @param {boolean} useUtc Indica se usare l'estensione utc di Dayjs per parsare la data o no
778
- *
779
- * @returns {any} Data in modalità DayJs
780
- */
781
- getDateConvertion(date, useUtc = false) {
782
- if (!date)
783
- return null;
784
- if (this.isDayJs(date))
785
- return date;
786
- if (this.isJsDate(date))
787
- return useUtc ? dayjs.utc(date) : dayjs(date);
788
- // Se non c'è la proprietà length vuol dire che non è una stringa e a questo punto non ho idea di che minchia sia
789
- let length = date.length;
790
- if (!length)
791
- return null;
792
- if (useUtc && !this.allowUtc)
793
- throw "@esfaenza/extensions: Richiesta data UTC ma da configurazione la libreria non lo supporta";
794
- // Per risparmiare chiamate a tentoni, in base alla lunghezza della stringa chiamo direttamente il metodo giusto,
795
- // controllando poi che mi generi una data come me la aspetto io
796
- let tryThis = null;
797
- let toCall = useUtc ? dayjs.utc : dayjs;
798
- // Se la data contiene la lettera "T" vuol dire che è una data del tipo 2020-10-15T00:00:00, direttamente parsabile dal dayjs col locale corretto.
799
- // Per il resto mi baso sulla lunghezza della stringa
800
- if (date.includes("T"))
801
- tryThis = toCall(date);
802
- else if (length == this.cfg.fulldate.length)
803
- tryThis = toCall(date, this.cfg.fulldate);
804
- else if (length == this.cfg.smallDate.length)
805
- tryThis = toCall(date, this.cfg.smallDate);
806
- else if (length == this.cfg.fulldateNoSS.length)
807
- tryThis = toCall(date, this.cfg.fulldateNoSS);
808
- if (tryThis && tryThis.isValid && tryThis.isValid())
809
- return tryThis;
810
- // Se niente funziona, null.
811
- // Ricondurre l'Input ad una data non è possibile.
812
- return null;
813
- }
814
- /**
815
- * Helper che restituisce **true** qualora l'oggetto passato fosse una data vera e propria, **false** altrimenti
816
- *
817
- * @param {any} check Oggetto da controllare
818
- *
819
- * @returns {boolean} Indicazione se l'oggetto è un vero Date di Javascript o no
820
- */
821
- isJsDate(check) {
822
- return check && Object.prototype.toString.call(check) === "[object Date]" && !isNaN(check);
823
- }
824
- /**
825
- * Helper che restituisce **true** qualora l'oggetto passato fosse un DayJs vero e proprio, **false** altrimenti
826
- *
827
- * @param {any} check Oggetto da controllare
828
- * @returns {boolean} Indicazione se l'oggetto è un vero DayJs o no
829
- */
830
- isDayJs(check) {
831
- // Non uso instanceof perché sarebbe estremamente più lento
832
- // Non uso nemmeno isDayjs() della libreria Dayjs dato che ha lo stesso problema: https://github.com/iamkun/dayjs/blob/06f88f425828b1ce96b737332d25145a95a4ee9d/src/index.js#L9
833
- return check.$D !== undefined && check.$M !== undefined && check.$y !== undefined;
834
- }
835
- /**
836
- * Ottiene la formattazione di una data in base alla versione richiesta "small" o "long" e a se stampare o meno i secondi
837
- *
838
- * @param {any} date Oggetto rappresentante una data. Potrebbe essere una stringa, un Date o già un DayJs
839
- * @param {boolean} small Indica se formattarla con un formato breve (se **true**) o un formato più lungo (se **false**)
840
- *
841
- * @returns {string} Data formattata nel formato richiesto
842
- */
843
- getFormatted(date, small, seconds) {
844
- var date = this.getDateConvertion(date);
845
- if (!date)
846
- return null;
847
- return date.format(small ? this.cfg.smallDate : seconds ? this.cfg.fulldateDisplay : this.cfg.fulldateDisplayNoSS);
848
299
  }
849
300
  }
850
- DateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService, deps: [{ token: EXT_LOCALE, optional: true }, { token: EXT_ALLOW_UTC, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
851
- DateServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService });
852
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DateService, decorators: [{
853
- type: Injectable
854
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
855
- type: Optional
856
- }, {
857
- type: Inject,
858
- args: [EXT_LOCALE]
859
- }] }, { type: undefined, decorators: [{
860
- type: Optional
861
- }, {
862
- type: Inject,
863
- args: [EXT_ALLOW_UTC]
864
- }] }]; } });
301
+ ExtensionsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
302
+ ExtensionsModulemod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, declarations: [LocDatePipe], imports: [LocalizationModule], exports: [LocDatePipe] });
303
+ ExtensionsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, imports: [LocalizationModule] });
304
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExtensionsModule, decorators: [{
305
+ type: NgModule,
306
+ args: [{
307
+ imports: [LocalizationModule],
308
+ declarations: [LocDatePipe],
309
+ exports: [LocDatePipe]
310
+ }]
311
+ }] });
865
312
 
866
313
  /**
867
314
  * Suffisso della proprietà da generare in cui salvare il navigatore
@@ -1005,413 +452,993 @@ const ExportAs = (order, type, prefix) => (target, propertyKey) => {
1005
452
  target[exportList] = [propertyKey + prop + csvVisPrefix];
1006
453
  }
1007
454
  }
1008
- };
1009
- /**
1010
- * Decoratore di esportazione utilizzabile su una lista,
1011
- * si utilizzano 2 navigator rispettivamente per la valorizzazione dell'Header e del Valore e
1012
- * per ogni elemento dell'array verranno generate 3 proprietà che gestiranno l'esportazione: (prop, prop_csvvis, prop_csvhdr)
1013
- *
1014
- * ATTENZIONE: Gli oggetti esportati in questa modalità non devono presentare altri tag di tipo @Export{...} all'interno. Fa tutto questo decoratore
1015
- *
1016
- * I navigator hanno questa convenzione:
1017
- *
1018
- * . --> divide due proprietà da richiamare consecutivamente: "a.b" richiamerà "obj.a.b";
1019
- *
1020
- * | --> divide più proprietà per le concatenazioni con uno spazio fra una e l'altra: "regdesc|slice.slicename" diventerà obj.regdesc + " " + obj.slice.slicename
1021
- *
1022
- * @param {number} order Ordine di esportazione. Più piccolo è, prima appare il valore
1023
- * @param {string} headernavigator Navigator col compito di selezionare il titolo per la colonna
1024
- * @param {string} valuenavigator Navigator col compito di selezionare il valore per la colonna
1025
- * @param {string} valueformat Formato di esportazione per i valori selezionati dal **valuenavigator**. Può essere un formato data per i campi data o 'F' per i campi floating point
1026
- */
1027
- const ExportList = (order, headernavigator, valuenavigator, valueformat = null) => (target, propertyKey) => {
1028
- Object.defineProperty(target, propertyKey + csvOrdPrefix, { value: order, writable: false });
1029
- Object.defineProperty(target, propertyKey + listCsvValPrefix, { value: valuenavigator, writable: false });
1030
- Object.defineProperty(target, propertyKey + listCsvHdrPrefix, { value: headernavigator, writable: false });
1031
- Object.defineProperty(target, propertyKey + listCsvValFormat, { value: valueformat, writable: false });
1032
- if (target[exportList])
1033
- target[exportList].push(propertyKey + listCsvHdrPrefix);
1034
- else
1035
- target[exportList] = [propertyKey + listCsvHdrPrefix];
1036
- };
1037
- /**
1038
- * Decoratore di esportazione utilizzabile su un dizionario. Se ad esempio si vogliono generare le colonne
1039
- *
1040
- * Valore A = 1
1041
- *
1042
- * Valore B = 2
1043
- *
1044
- * Valore C = 3
1045
- *
1046
- * partendo da un oggetto iniziale { Valore { A: 1, B: 2, C: 3 } }
1047
- *
1048
- * @param {number} order Ordine di esportazione. Più piccolo è, prima appare il valore
1049
- * @param {string} headernavigator Navigator col compito di selezionare il titolo per la colonna
1050
- * @param {string} valuenavigator Navigator col compito di selezionare il valore per la colonna
1051
- */
1052
- const ExportDictionary = (order, headernavigator, valuenavigator) => (target, propertyKey) => {
1053
- Object.defineProperty(target, propertyKey + csvOrdPrefix, { value: order, writable: false });
1054
- Object.defineProperty(target, propertyKey + dictionaryCsvValPrefix, { value: valuenavigator, writable: false });
1055
- Object.defineProperty(target, propertyKey + dictionaryCsvHdrPrefix, { value: headernavigator, writable: false });
1056
- if (target[exportList])
1057
- target[exportList].push(propertyKey + dictionaryCsvHdrPrefix);
1058
- else
1059
- target[exportList] = [propertyKey + dictionaryCsvHdrPrefix];
1060
- };
1061
- /**
1062
- * Service che si occupa dell'esportazione degli oggetti marcati dai Decoratori **ExportDictionary**, **ExportList**, **ExportAs** o **Export**
1063
- */
1064
- class ExportService {
455
+ };
456
+ /**
457
+ * Decoratore di esportazione utilizzabile su una lista,
458
+ * si utilizzano 2 navigator rispettivamente per la valorizzazione dell'Header e del Valore e
459
+ * per ogni elemento dell'array verranno generate 3 proprietà che gestiranno l'esportazione: (prop, prop_csvvis, prop_csvhdr)
460
+ *
461
+ * ATTENZIONE: Gli oggetti esportati in questa modalità non devono presentare altri tag di tipo @Export{...} all'interno. Fa tutto questo decoratore
462
+ *
463
+ * I navigator hanno questa convenzione:
464
+ *
465
+ * . --> divide due proprietà da richiamare consecutivamente: "a.b" richiamerà "obj.a.b";
466
+ *
467
+ * | --> divide più proprietà per le concatenazioni con uno spazio fra una e l'altra: "regdesc|slice.slicename" diventerà obj.regdesc + " " + obj.slice.slicename
468
+ *
469
+ * @param {number} order Ordine di esportazione. Più piccolo è, prima appare il valore
470
+ * @param {string} headernavigator Navigator col compito di selezionare il titolo per la colonna
471
+ * @param {string} valuenavigator Navigator col compito di selezionare il valore per la colonna
472
+ * @param {string} valueformat Formato di esportazione per i valori selezionati dal **valuenavigator**. Può essere un formato data per i campi data o 'F' per i campi floating point
473
+ */
474
+ const ExportList = (order, headernavigator, valuenavigator, valueformat = null) => (target, propertyKey) => {
475
+ Object.defineProperty(target, propertyKey + csvOrdPrefix, { value: order, writable: false });
476
+ Object.defineProperty(target, propertyKey + listCsvValPrefix, { value: valuenavigator, writable: false });
477
+ Object.defineProperty(target, propertyKey + listCsvHdrPrefix, { value: headernavigator, writable: false });
478
+ Object.defineProperty(target, propertyKey + listCsvValFormat, { value: valueformat, writable: false });
479
+ if (target[exportList])
480
+ target[exportList].push(propertyKey + listCsvHdrPrefix);
481
+ else
482
+ target[exportList] = [propertyKey + listCsvHdrPrefix];
483
+ };
484
+ /**
485
+ * Decoratore di esportazione utilizzabile su un dizionario. Se ad esempio si vogliono generare le colonne
486
+ *
487
+ * Valore A = 1
488
+ *
489
+ * Valore B = 2
490
+ *
491
+ * Valore C = 3
492
+ *
493
+ * partendo da un oggetto iniziale { Valore { A: 1, B: 2, C: 3 } }
494
+ *
495
+ * @param {number} order Ordine di esportazione. Più piccolo è, prima appare il valore
496
+ * @param {string} headernavigator Navigator col compito di selezionare il titolo per la colonna
497
+ * @param {string} valuenavigator Navigator col compito di selezionare il valore per la colonna
498
+ */
499
+ const ExportDictionary = (order, headernavigator, valuenavigator) => (target, propertyKey) => {
500
+ Object.defineProperty(target, propertyKey + csvOrdPrefix, { value: order, writable: false });
501
+ Object.defineProperty(target, propertyKey + dictionaryCsvValPrefix, { value: valuenavigator, writable: false });
502
+ Object.defineProperty(target, propertyKey + dictionaryCsvHdrPrefix, { value: headernavigator, writable: false });
503
+ if (target[exportList])
504
+ target[exportList].push(propertyKey + dictionaryCsvHdrPrefix);
505
+ else
506
+ target[exportList] = [propertyKey + dictionaryCsvHdrPrefix];
507
+ };
508
+ /**
509
+ * Service che si occupa dell'esportazione degli oggetti marcati dai Decoratori **ExportDictionary**, **ExportList**, **ExportAs** o **Export**
510
+ */
511
+ class ExportService {
512
+ /**
513
+ * Costruttore
514
+ *
515
+ * @ignore
516
+ */
517
+ constructor(dates) {
518
+ this.dates = dates;
519
+ }
520
+ /**
521
+ * Data una lista di oggetti genera gli header per l'esportazione e integra ogni oggetto con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
522
+ *
523
+ * @param {any[]} objs Oggetti da esportare
524
+ *
525
+ * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
526
+ */
527
+ setupForExport(objs, columns = []) {
528
+ let headers = [];
529
+ let headersCache = {};
530
+ if (!objs || objs.length == 0)
531
+ return headers;
532
+ // Devo per forza farlo per tutti gli oggetti dato che devo esporre le proprietà che mi servono da esportare
533
+ // Gli header li raccolgo tutti poi faccio uan distinct in modo da esportare tutte le proprietà per bene
534
+ let columnSelectionDictionary = {};
535
+ if (columns?.length > 0)
536
+ for (let i = 0; i < columns?.length; i++)
537
+ columnSelectionDictionary[columns[i]] = true;
538
+ for (let i = 0; i < objs.length; i++) {
539
+ let hds = this.parseAndGetHeaders(objs[i], columnSelectionDictionary);
540
+ // Aggiungo gli header facendo in modo di non aggiungerne mai due uguali per non dover fare una distinct dopo
541
+ // che costerebbe comunque risorse. Utilizzo un dizionario d'appoggio per lookup istantanei
542
+ for (let k = 0; k < hds.length; k++) {
543
+ if (!headersCache[hds[k].key]) {
544
+ headersCache[hds[k].key] = true;
545
+ headers.push(hds[k]);
546
+ }
547
+ }
548
+ }
549
+ if (!columns || columns.length == 0)
550
+ return headers.sort((a, b) => a.order - b.order);
551
+ else {
552
+ // Se mi arriva la lista di colonne non mi dà solo la visibilità, ma anche l'ordinamento
553
+ // le esporto di conseguenza
554
+ let orderedHeaders = [];
555
+ for (let i = 0; i < columns.length; i++) {
556
+ let thisId = columns[i];
557
+ for (let i = 0; i < headers.length; i++) {
558
+ if (headers[i].propKey == thisId) {
559
+ orderedHeaders.push(headers[i]);
560
+ break;
561
+ }
562
+ }
563
+ }
564
+ return orderedHeaders;
565
+ }
566
+ }
567
+ /**
568
+ * Data una lista di oggetti generici genera gli header per l'esportazione e integra ogni oggetto con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
569
+ *
570
+ * @param {GenericItem[]} objs Oggetti generici da esportare
571
+ * @param {CsvHeaders[]} columns Colonne richieste dall'esportazione
572
+ *
573
+ * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
574
+ */
575
+ setupForGenericExport(objs, columns) {
576
+ let headers = [];
577
+ if (!objs || objs.length == 0)
578
+ return headers;
579
+ // Devo per forza farlo per tutti gli oggetti dato che devo esporre le proprietà che mi servono da esportare
580
+ objs.forEach((obj, index) => {
581
+ if (index == 0)
582
+ headers = this.parseAndGetGenericHeaders(obj, columns);
583
+ else
584
+ this.parseAndGetGenericHeaders(obj, columns);
585
+ });
586
+ return headers;
587
+ }
588
+ /**
589
+ * Dato un oggetto generico genera gli header per l'esportazione e lo integra con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
590
+ *
591
+ * Metodo interno richiamato da **setupForGenericExport**
592
+ *
593
+ * @param {GenericItem} obj Oggetto generico da esportare
594
+ * @param {CsvColumn[]} columns Colonne richieste dall'esportazione
595
+ *
596
+ * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
597
+ */
598
+ parseAndGetGenericHeaders(obj, columns) {
599
+ if (!obj.properties)
600
+ return null;
601
+ var headers = [];
602
+ //Miracle incoming
603
+ columns.forEach(t => {
604
+ var propValue = obj.properties[t.key];
605
+ Object.defineProperty(obj, t.key + "_exp", { value: propValue, writable: false, enumerable: true });
606
+ headers.push({ label: t.label, key: t.key + "_exp", order: t.order, type: t.type, propKey: null });
607
+ });
608
+ //Tirati fuori tutti gli header faccio sort sull'ordinamento
609
+ return headers.sort((a, b) => { return a.order - b.order; });
610
+ }
611
+ /**
612
+ * Dato un oggetto genera gli header per l'esportazione e lo integra con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
613
+ *
614
+ * Metodo interno richiamato da **setupForExport**
615
+ *
616
+ * @param {any} obj Oggetto generico da esportare
617
+ *
618
+ * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
619
+ */
620
+ parseAndGetHeaders(obj, columnsSelection) {
621
+ var headers = [];
622
+ // Miracle incoming
623
+ let exportProps = obj[exportList] || [];
624
+ if (exportProps.length == 0)
625
+ console.warn("[@esfaenza/extensions] Nessuna colonna configurata da esportare!");
626
+ for (let i = 0; i < exportProps.length; i++) {
627
+ let prop = exportProps[i];
628
+ let propBase = "";
629
+ let toCall;
630
+ if (prop.endsWith(listCsvHdrPrefix)) {
631
+ propBase = prop.replace(listCsvHdrPrefix, '');
632
+ toCall = this.parseList.bind(this);
633
+ }
634
+ else if (prop.endsWith(dictionaryCsvHdrPrefix)) {
635
+ toCall = this.parseDictionary.bind(this);
636
+ propBase = prop.replace(dictionaryCsvHdrPrefix, '');
637
+ }
638
+ else if (prop.endsWith(csvVisPrefix)) {
639
+ propBase = prop.replace(csvVisPrefix, '');
640
+ if (typeof obj[propBase + csvValueNavigator] !== "undefined")
641
+ toCall = this.parseObject.bind(this);
642
+ else
643
+ toCall = this.parseProperty.bind(this);
644
+ }
645
+ if (toCall)
646
+ toCall(obj, propBase, headers, columnsSelection);
647
+ }
648
+ return headers;
649
+ }
650
+ /**
651
+ * Setup per l'esportazione di una singola proprietà.
652
+ * Viene generato l'header e la proprietà relativa al valore formattato o no
653
+ *
654
+ * @param {any} obj Oggetto di cui esportare la proprietà **prop**
655
+ * @param {string} prop Proprietà da esportare
656
+ * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
657
+ */
658
+ parseProperty(obj, prop, headers, columnsSelection) {
659
+ let toEmit = obj[prop + csvVisPrefix];
660
+ let format = obj[prop + csvFormatPrefix];
661
+ let propKey = obj[prop + csvPropKey];
662
+ if (propKey && !columnsSelection[propKey] && Object.keys(columnsSelection).length > 0) {
663
+ console.log("[@esfaenza/extensions] Colonna da non esportare: " + propKey);
664
+ return;
665
+ }
666
+ let lbl = obj[prop + csvHdrPrefix];
667
+ let order = obj[prop + csvOrdPrefix];
668
+ if (toEmit && lbl !== "undefined") {
669
+ // Se non ho un formato esporto direttamente
670
+ if (!format) {
671
+ // Se è un formato data mi assicuro di non stampare mai una data 00:00:00 altrimenti in esportazione viene uno schifo
672
+ var val = obj[prop] ? this.dates.trimSeconds(obj[prop].toString()) : "";
673
+ Object.defineProperty(obj, prop + "_nofmt", { value: val, writable: false, enumerable: true });
674
+ headers.push({ label: lbl ? lbl : prop + "_nofmt", key: prop + "_nofmt", order: order, type: "string", propKey: propKey });
675
+ }
676
+ else {
677
+ // Caso formato numerico
678
+ if (format == "F") {
679
+ // Check considerando la possibilità di zeri
680
+ let val = obj[prop] != null ? obj[prop].toString() : "";
681
+ // Elimino tutte le virgole (indicatori di migliaia per la culture default en-US)
682
+ val = val.replace(/,/, "");
683
+ // Il singolo punto che c'è, se presente (separatore decimali) lo trasformo in virgola
684
+ let valConverted = val.replace(".", ",");
685
+ Object.defineProperty(obj, prop + "_fmt", { value: valConverted, writable: false, enumerable: true });
686
+ headers.push({ label: lbl ? lbl : prop + "_fmt", key: prop + "_fmt", order: order, type: "number", propKey: propKey });
687
+ }
688
+ else {
689
+ // Se ho un formato data trasformo in dayjs, formatto ed esporto
690
+ // Per la trasformazione in dayjs provo prima a passargli la stringa di netto, sperando la riconosca
691
+ // Se non funziona provo a definirgli la formattazione standard del metering per quel linguaggio
692
+ let val = this.dates.getFormatted(obj[prop], format.length <= 10, format.length > 16);
693
+ Object.defineProperty(obj, prop + "_fmt", { value: val ?? "", writable: false, enumerable: true });
694
+ headers.push({ label: lbl ? lbl : prop + "_fmt", key: prop + "_fmt", order: order, type: "date", propKey: propKey });
695
+ }
696
+ }
697
+ }
698
+ }
699
+ /**
700
+ * Setup per l'esportazione di una singola proprietà da un oggetto.
701
+ * Viene generato l'header e la proprietà relativa al valore formattato o no.
702
+ *
703
+ * Per ottenere il valore e l'header della proprietà vengono utilizzati i navigatori auto-generati nel decoratore **ExportAs**
704
+ *
705
+ * @param {any} obj Oggetto di cui esportare la proprietà **prop**
706
+ * @param {string} prop Proprietà da esportare
707
+ * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
708
+ */
709
+ parseObject(obj, prop, headers) {
710
+ //se questa proprietà ha un navigator significa che deriva da un oggetto interno, recupero il valore in questa fase dato che prima non ce l'ho
711
+ let valuenavigator = obj[prop + csvValueNavigator];
712
+ //Es. di valuenavigator obj.subobj.subsubobj.property
713
+ let jmps = valuenavigator.split(".");
714
+ //Mi clono l'oggetto originale per non uccidergli i riferimenti
715
+ let clone = JSON.parse(JSON.stringify(obj));
716
+ //Salto ricorsivamente su me stesso per arrivare alla proprietà
717
+ //Salto 1 > diventa obj
718
+ //Salto 2 > diventa obj.subobj
719
+ //ecc...
720
+ jmps.forEach(pp => { clone = clone[pp]; });
721
+ //Valore finale :D
722
+ let value = clone;
723
+ let lbl = obj[prop + csvHdrPrefix];
724
+ let order = obj[prop + csvOrdPrefix];
725
+ //Se per questa proprietà ho un formato lo applico
726
+ let format = obj[prop + csvFormatPrefix];
727
+ let type = "string";
728
+ if (format == "F") {
729
+ let val = value.toString();
730
+ val = val.replace(/,/, "");
731
+ value = val.replace(".", ",");
732
+ type = "number";
733
+ }
734
+ else if (format) {
735
+ value = this.dates.getFormatted(value, format.length <= 10, format.length > 16);
736
+ type = "date";
737
+ }
738
+ Object.defineProperty(obj, prop, { value: value ?? "", writable: false, enumerable: true });
739
+ headers.push({ label: lbl ? lbl : prop, key: prop, order: order, type: type, propKey: null });
740
+ }
741
+ /**
742
+ * Setup per l'esportazione di una lista di valori pivottati dinamicamente:
743
+ *
744
+ * => Recupero il valore utilizzando i navigatori
745
+ *
746
+ * => Recupero l'header utilizzando i navigatori
747
+ *
748
+ * => Con il valore ci creo la property
749
+ *
750
+ * => Con l'header e il riferimento alla property appena creata creo un record negli header da esportare
751
+ *
752
+ * @param {any} obj Oggetto di cui esportare la proprietà **prop**
753
+ * @param {string} prop Proprietà da esportare
754
+ * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
755
+ */
756
+ parseList(obj, prop, headers) {
757
+ let headernavigator = obj[prop + listCsvHdrPrefix];
758
+ let valuenavigator = obj[prop + listCsvValPrefix];
759
+ let valueformat = obj[prop + listCsvValFormat];
760
+ let order = obj[prop + csvOrdPrefix];
761
+ var jumps, tmpRes;
762
+ var objs = obj[prop];
763
+ for (let idx = 0; idx < objs.length; idx++) {
764
+ let listitem = objs[idx];
765
+ var headersplit = headernavigator.split(" ");
766
+ var header = "";
767
+ // Per l'header si considera a se stante ogni pezzo separato da spazio per fare le concatenazioni
768
+ headersplit.forEach(pr => {
769
+ jumps = pr.split(".");
770
+ tmpRes = JSON.parse(JSON.stringify(listitem));
771
+ jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
772
+ if (!header)
773
+ header = tmpRes;
774
+ else
775
+ header += " " + tmpRes;
776
+ });
777
+ jumps = valuenavigator.split(".");
778
+ tmpRes = JSON.parse(JSON.stringify(listitem));
779
+ jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
780
+ let value = tmpRes;
781
+ let type = "string";
782
+ if (valueformat == "F") {
783
+ let val = value != null ? value.toString() : "";
784
+ val = val.replace(/,/, "");
785
+ value = val.replace(".", ",");
786
+ type = "number";
787
+ }
788
+ else if (valueformat) {
789
+ value = this.dates.getFormatted(obj[prop], valueformat.length <= 10, valueformat.length > 16);
790
+ type = "date";
791
+ }
792
+ Object.defineProperty(obj, prop + idx, { value: value ?? "", writable: false });
793
+ headers.push({ label: header, key: prop + idx, order: order + idx, type: type, propKey: null });
794
+ }
795
+ }
796
+ /**
797
+ * Setup per l'esportazione di una lista di valori pivottati dinamicamente:
798
+ *
799
+ * => Recupero il valore utilizzando i navigatori
800
+ *
801
+ * => Recupero l'header utilizzando i navigatori
802
+ *
803
+ * => Con il valore ci creo la property
804
+ *
805
+ * => Con l'header e il riferimento alla property appena creata creo un record negli header da esportare
806
+ *
807
+ * @param {any} obj Oggetto di cui esportare la proprietà **prop**
808
+ * @param {string} prop Proprietà da esportare
809
+ * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
810
+ */
811
+ parseDictionary(obj, prop, headers) {
812
+ let headernavigator = obj[prop + dictionaryCsvHdrPrefix];
813
+ let valuenavigator = obj[prop + dictionaryCsvValPrefix];
814
+ let order = obj[prop + csvOrdPrefix];
815
+ var jumps, tmpRes;
816
+ var objs = obj[prop];
817
+ for (let idx = 0; idx < objs.length; idx++) {
818
+ let listitem = objs[idx];
819
+ var headersplit = headernavigator.split(" ");
820
+ var header = "";
821
+ headersplit.forEach(pr => {
822
+ jumps = pr.split(".");
823
+ tmpRes = JSON.parse(JSON.stringify(listitem));
824
+ jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
825
+ if (!header)
826
+ header = tmpRes;
827
+ else
828
+ header += " " + tmpRes;
829
+ });
830
+ var valuesplit = valuenavigator.split(",");
831
+ valuesplit.forEach((t, idxinternal) => {
832
+ var navig = t.split("-")[1];
833
+ var desc = t.split("-")[0];
834
+ jumps = navig.split(".");
835
+ tmpRes = JSON.parse(JSON.stringify(listitem));
836
+ jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
837
+ let value = tmpRes;
838
+ let propname = prop + idx.toString() + idxinternal.toString();
839
+ Object.defineProperty(obj, propname, { value: value ?? "", writable: false });
840
+ headers.push({ label: header + " " + desc, key: propname, order: order + idx, type: 'string', propKey: null });
841
+ });
842
+ }
843
+ }
844
+ /**
845
+ * Metodo che si occupa della deserializzazione degli oggetti da esportare. Messo a livello di libreria in modo che qualora ci fosse da cambiare libreria
846
+ * tutte le manutenzioni siano concentrate qui
847
+ *
848
+ * @param {any[]} items Oggetti da deserializzare
849
+ * @param {any} type Tipo da usare come prototipo per la deserializzazione
850
+ *
851
+ * @returns {any} Oggetti deserializzati
852
+ */
853
+ deserializeForExport(items, type) {
854
+ return Deserialize(items, type);
855
+ }
856
+ }
857
+ ExportService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService, deps: [{ token: DateService }], target: i0.ɵɵFactoryTarget.Injectable });
858
+ ExportService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService, providedIn: "root" });
859
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService, decorators: [{
860
+ type: Injectable,
861
+ args: [{ providedIn: "root" }]
862
+ }], ctorParameters: function () { return [{ type: DateService }]; } });
863
+
864
+ // Angular
865
+ /**
866
+ * Service che fornisce funzionalità di alert nei tipi fondamentali: success, info, warning e danger
867
+ */
868
+ class MessageService {
869
+ /**
870
+ * Costruttore
871
+ *
872
+ * @ignore
873
+ */
874
+ constructor(injector, locProvider) {
875
+ this.injector = injector;
876
+ this.locProvider = locProvider;
877
+ /**
878
+ * Oggetto statico in supporto alla localizzazione
879
+ */
880
+ this.loc = {
881
+ 'Confirm': { 'en-US': "Confirm", 'it-IT': "Conferma", },
882
+ 'Cancel': { 'en-US': "Cancel", 'it-IT': "Annulla", },
883
+ 'Close': { 'en-US': "Close", 'it-IT': "Chiudi", },
884
+ 'Error': { 'en-US': "Error", 'it-IT': "Errore", },
885
+ 'Info': { 'en-US': "Info", 'it-IT': "Informazione", },
886
+ 'Success': { 'en-US': "Success", 'it-IT': "Successo", },
887
+ 'Warning': { 'en-US': "Warning", 'it-IT': "Attenzione", }
888
+ };
889
+ this.init();
890
+ }
891
+ get toastr() {
892
+ return this.injector.get(ToastrService);
893
+ }
894
+ async init() {
895
+ let LOCALE = this.locProvider ? await this.locProvider.Locale.pipe(first()).toPromise() : 'it-IT';
896
+ this.expiredSessionSwal = swal.mixin({
897
+ showCancelButton: false,
898
+ confirmButtonText: 'Login',
899
+ customClass: {
900
+ confirmButton: "btn btn-primary",
901
+ container: "app-wrap",
902
+ },
903
+ buttonsStyling: false,
904
+ reverseButtons: false,
905
+ allowOutsideClick: false,
906
+ icon: "warning"
907
+ });
908
+ this.successSwalWithCancel = swal.mixin({
909
+ title: this.loc["Success"][LOCALE],
910
+ customClass: {
911
+ confirmButton: "btn btn-primary",
912
+ cancelButton: "btn btn-secondary app-margin-right-10",
913
+ container: "app-wrap",
914
+ },
915
+ buttonsStyling: false,
916
+ reverseButtons: true,
917
+ showCancelButton: true,
918
+ confirmButtonText: this.loc["Confirm"][LOCALE],
919
+ cancelButtonText: this.loc["Cancel"][LOCALE],
920
+ icon: "success"
921
+ });
922
+ this.warningSwalWithCancel = swal.mixin({
923
+ title: this.loc["Warning"][LOCALE],
924
+ customClass: {
925
+ confirmButton: "btn btn-primary",
926
+ cancelButton: "btn btn-secondary app-margin-right-10",
927
+ container: "app-wrap",
928
+ },
929
+ buttonsStyling: false,
930
+ reverseButtons: true,
931
+ showCancelButton: true,
932
+ confirmButtonText: this.loc["Confirm"][LOCALE],
933
+ cancelButtonText: this.loc["Cancel"][LOCALE],
934
+ icon: "warning"
935
+ });
936
+ this.warningSwal = swal.mixin({
937
+ title: this.loc["Warning"][LOCALE],
938
+ customClass: {
939
+ confirmButton: "btn btn-primary",
940
+ container: "app-wrap",
941
+ },
942
+ buttonsStyling: false,
943
+ reverseButtons: true,
944
+ showCancelButton: false,
945
+ confirmButtonText: this.loc["Close"][LOCALE],
946
+ icon: "warning"
947
+ });
948
+ this.successSwal = swal.mixin({
949
+ title: this.loc["Success"][LOCALE],
950
+ customClass: {
951
+ confirmButton: "btn btn-primary",
952
+ container: "app-wrap",
953
+ },
954
+ buttonsStyling: false,
955
+ reverseButtons: true,
956
+ showCancelButton: false,
957
+ confirmButtonText: this.loc["Close"][LOCALE],
958
+ icon: "success"
959
+ });
960
+ this.errorSwal = swal.mixin({
961
+ title: this.loc["Error"][LOCALE],
962
+ customClass: {
963
+ confirmButton: "btn btn-primary",
964
+ container: "app-wrap",
965
+ },
966
+ buttonsStyling: false,
967
+ reverseButtons: true,
968
+ showCancelButton: false,
969
+ confirmButtonText: this.loc["Close"][LOCALE],
970
+ icon: "error"
971
+ });
972
+ this.infoSwal = swal.mixin({
973
+ title: this.loc["Info"][LOCALE],
974
+ customClass: {
975
+ confirmButton: "btn btn-primary",
976
+ container: "app-wrap",
977
+ },
978
+ buttonsStyling: false,
979
+ reverseButtons: true,
980
+ showCancelButton: false,
981
+ confirmButtonText: this.loc["Close"][LOCALE],
982
+ icon: "info"
983
+ });
984
+ }
985
+ /**
986
+ * Presentazione di un semplice messaggio di successo
987
+ *
988
+ * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
989
+ * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
990
+ *
991
+ * @param {string} text Testo da mostrare
992
+ * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
993
+ */
994
+ simpleSuccess(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
995
+ let tos = this.toastrOrSwal(secondaryNotificationType);
996
+ if (tos == "swal")
997
+ this.successSwal.fire("", text);
998
+ else
999
+ this.toastr.success(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
1000
+ }
1001
+ /**
1002
+ * Presentazione di un semplice messaggio di informazione
1003
+ *
1004
+ * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
1005
+ * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
1006
+ *
1007
+ * @param {string} text Testo da mostrare
1008
+ * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
1009
+ */
1010
+ simpleInfo(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
1011
+ let tos = this.toastrOrSwal(secondaryNotificationType);
1012
+ if (tos == "swal")
1013
+ this.infoSwal.fire("", text);
1014
+ else
1015
+ this.toastr.info(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
1016
+ }
1065
1017
  /**
1066
- * Costruttore
1018
+ * Presentazione di un semplice messaggio di errore
1067
1019
  *
1068
- * @ignore
1020
+ * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
1021
+ * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
1022
+ *
1023
+ * @param {string} text Testo da mostrare
1024
+ * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
1069
1025
  */
1070
- constructor(dates) {
1071
- this.dates = dates;
1026
+ simpleError(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
1027
+ let tos = this.toastrOrSwal(secondaryNotificationType);
1028
+ if (tos == "swal")
1029
+ this.errorSwal.fire("", text);
1030
+ else
1031
+ this.toastr.error(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
1072
1032
  }
1073
1033
  /**
1074
- * Data una lista di oggetti genera gli header per l'esportazione e integra ogni oggetto con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
1034
+ * Presentazione di un semplice messaggio di avviso
1075
1035
  *
1076
- * @param {any[]} objs Oggetti da esportare
1036
+ * Se viene chiamato con le impostazioni di default e toastr è abilitato, viene mostrato al centro
1037
+ * Se viene chiamato con override su toastr vuol dire che è una notifica secondaria e appare in alto a destra
1077
1038
  *
1078
- * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
1039
+ * @param {string} text Testo da mostrare
1040
+ * @param {"toastr" | "swal" | null} secondaryNotificationType Qualora sia una notifica secondaria, ne indica il tipo
1079
1041
  */
1080
- setupForExport(objs, columns = []) {
1081
- let headers = [];
1082
- let headersCache = {};
1083
- if (!objs || objs.length == 0)
1084
- return headers;
1085
- // Devo per forza farlo per tutti gli oggetti dato che devo esporre le proprietà che mi servono da esportare
1086
- // Gli header li raccolgo tutti poi faccio uan distinct in modo da esportare tutte le proprietà per bene
1087
- let columnSelectionDictionary = {};
1088
- if (columns?.length > 0)
1089
- for (let i = 0; i < columns?.length; i++)
1090
- columnSelectionDictionary[columns[i]] = true;
1091
- for (let i = 0; i < objs.length; i++) {
1092
- let hds = this.parseAndGetHeaders(objs[i], columnSelectionDictionary);
1093
- // Aggiungo gli header facendo in modo di non aggiungerne mai due uguali per non dover fare una distinct dopo
1094
- // che costerebbe comunque risorse. Utilizzo un dizionario d'appoggio per lookup istantanei
1095
- for (let k = 0; k < hds.length; k++) {
1096
- if (!headersCache[hds[k].key]) {
1097
- headersCache[hds[k].key] = true;
1098
- headers.push(hds[k]);
1099
- }
1100
- }
1101
- }
1102
- if (!columns || columns.length == 0)
1103
- return headers.sort((a, b) => a.order - b.order);
1104
- else {
1105
- // Se mi arriva la lista di colonne non mi dà solo la visibilità, ma anche l'ordinamento
1106
- // le esporto di conseguenza
1107
- let orderedHeaders = [];
1108
- for (let i = 0; i < columns.length; i++) {
1109
- let thisId = columns[i];
1110
- for (let i = 0; i < headers.length; i++) {
1111
- if (headers[i].propKey == thisId) {
1112
- orderedHeaders.push(headers[i]);
1113
- break;
1114
- }
1115
- }
1116
- }
1117
- return orderedHeaders;
1118
- }
1042
+ simpleWarning(text, secondaryNotificationType = null, secondaryNotificationTimeout = null) {
1043
+ let tos = this.toastrOrSwal(secondaryNotificationType);
1044
+ if (tos == "swal")
1045
+ this.warningSwal.fire("", text);
1046
+ else
1047
+ this.toastr.warning(text, null, { positionClass: tos == "toastr" ? "toast-top-right" : "toast-top-center", timeOut: secondaryNotificationTimeout || (tos == "toastr" ? 0 : 5000), extendedTimeOut: tos == "toastr" ? 0 : 1000 });
1119
1048
  }
1120
1049
  /**
1121
- * Data una lista di oggetti generici genera gli header per l'esportazione e integra ogni oggetto con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
1050
+ * Ripulisce tutti i messaggi presenti in un dato momento sullo schermo
1051
+ */
1052
+ clearMessages() {
1053
+ this.toastr.clear();
1054
+ swal.close();
1055
+ }
1056
+ /**
1057
+ * Mostra un messaggio di avviso con richiesta di conferma
1122
1058
  *
1123
- * @param {GenericItem[]} objs Oggetti generici da esportare
1124
- * @param {CsvHeaders[]} columns Colonne richieste dall'esportazione
1059
+ * @param {string} title Titolo del messaggio
1060
+ * @param {string} text Contenuto del messaggio
1125
1061
  *
1126
- * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
1062
+ * @returns {Promise} Restituisce una Promise col risultato scelto dall'utente (conferma o no)
1127
1063
  */
1128
- setupForGenericExport(objs, columns) {
1129
- let headers = [];
1130
- if (!objs || objs.length == 0)
1131
- return headers;
1132
- // Devo per forza farlo per tutti gli oggetti dato che devo esporre le proprietà che mi servono da esportare
1133
- objs.forEach((obj, index) => {
1134
- if (index == 0)
1135
- headers = this.parseAndGetGenericHeaders(obj, columns);
1136
- else
1137
- this.parseAndGetGenericHeaders(obj, columns);
1064
+ promiseWarningWithChoice(title, text) {
1065
+ return this.warningSwalWithCancel.fire(title, text);
1066
+ }
1067
+ /**
1068
+ * In base alla configurazione attuale e all'eventuale override per la chiamata del caso indica se bisogna utilizzare toastr o sweetalert
1069
+ *
1070
+ * @param {"toastr" | "swal" | null} providerOverride Override alla modalità configurata
1071
+ *
1072
+ * @returns {"toastr" | "swal"} Indicazione su che provider di messaggi di avviso debba essere utilizzato
1073
+ */
1074
+ toastrOrSwal(providerOverride) {
1075
+ if (providerOverride != null)
1076
+ return providerOverride;
1077
+ return "swal";
1078
+ }
1079
+ /**
1080
+ * Mostra un messaggio di avviso con richiesta di conferma e gestisce il risultato chiamato i callback **onsuccess** o **onerror**
1081
+ *
1082
+ * @param {string} title Titolo del messaggio
1083
+ * @param {string} text Contenuto del messaggio
1084
+ * @param {Function} onsuccess Callback da chimare su conferma
1085
+ * @param {Function} onerror Callback da chimare su errori
1086
+ */
1087
+ simpleWarningWithChoice(title, text, onsuccess = null, onerror = null) {
1088
+ this.warningSwalWithCancel.fire(title, text).then((result) => {
1089
+ if (result.value && onsuccess)
1090
+ onsuccess();
1091
+ else if (onerror)
1092
+ onerror();
1138
1093
  });
1139
- return headers;
1140
1094
  }
1141
1095
  /**
1142
- * Dato un oggetto generico genera gli header per l'esportazione e lo integra con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
1096
+ * Mostra un messaggio di success con richiesta di conferma e gestisce il risultato chiamato i callback **onconfirm** o **onabort**
1143
1097
  *
1144
- * Metodo interno richiamato da **setupForGenericExport**
1098
+ * @param {string} title Titolo del messaggio
1099
+ * @param {string} text Contenuto del messaggio
1100
+ * @param {Function} onconfirm Callback da chimare su conferma
1101
+ * @param {Function} onabort Callback da chimare su annullamento
1102
+ * @param {string} confirmtext Testo del pulsante di azione
1103
+ * @param {string} aborttext Testo del pulsante di annullamento
1104
+ */
1105
+ simpleSuccessWithChoice(title, text, onconfirm = null, onabort = null, confirmtext = "", aborttext = '') {
1106
+ var configuration = {
1107
+ title: title,
1108
+ html: text
1109
+ };
1110
+ if (confirmtext)
1111
+ configuration["confirmButtonText"] = confirmtext;
1112
+ if (aborttext)
1113
+ configuration["cancelButtonText"] = aborttext;
1114
+ this.successSwalWithCancel.fire(configuration).then((result) => {
1115
+ if (result.value && onconfirm)
1116
+ onconfirm();
1117
+ else if (onabort)
1118
+ onabort();
1119
+ });
1120
+ }
1121
+ /**
1122
+ * Mostra un messaggio di avviso con richiesta di conferma e gestisce il risultato chiamato i callback **onsuccess** o **onerror**
1145
1123
  *
1146
- * @param {GenericItem} obj Oggetto generico da esportare
1147
- * @param {CsvColumn[]} columns Colonne richieste dall'esportazione
1124
+ * @param {string} title Titolo del messaggio
1125
+ * @param {string} text Contenuto del messaggio
1126
+ */
1127
+ observableSimpleWarningWithChoice(title, text) {
1128
+ return of(this.warningSwalWithCancel.fire(title, text)).pipe(map(t => !!t.value));
1129
+ }
1130
+ /**
1131
+ * Mostra un messaggio di avviso che la sessione corrente è scaduta e propone all'utente di navigare al login. Qualora l'utente decidesse di farlo
1132
+ * verrà chiamata la funzione di callback **onnavigate
1148
1133
  *
1149
- * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
1134
+ * @param {string} title Titolo del messaggio
1135
+ * @param {string} text Contenuto del messaggio
1136
+ * @param {Function} onnavigate Callback da chimare su navigazione in corso
1150
1137
  */
1151
- parseAndGetGenericHeaders(obj, columns) {
1152
- if (!obj.properties)
1153
- return null;
1154
- var headers = [];
1155
- //Miracle incoming
1156
- columns.forEach(t => {
1157
- var propValue = obj.properties[t.key];
1158
- Object.defineProperty(obj, t.key + "_exp", { value: propValue, writable: false, enumerable: true });
1159
- headers.push({ label: t.label, key: t.key + "_exp", order: t.order, type: t.type, propKey: null });
1138
+ expiredSessionMessage(title, text, onnavigate = null) {
1139
+ this.expiredSessionSwal.fire(title, text).then((result) => {
1140
+ if (result.value && onnavigate)
1141
+ onnavigate();
1160
1142
  });
1161
- //Tirati fuori tutti gli header faccio sort sull'ordinamento
1162
- return headers.sort((a, b) => { return a.order - b.order; });
1163
1143
  }
1164
1144
  /**
1165
- * Dato un oggetto genera gli header per l'esportazione e lo integra con le proprietà esportabili dalla libreria **@ctrl/ngx-csv**
1145
+ * Metodo helper per gestire in maniera standardizzata una risposta di tipo **CallResult**
1146
+ *
1147
+ * @param {CallResult} response Risposta ricevuta dal server
1148
+ * @param {string} successMsg Messaggio da visualizzare in caso di successo
1149
+ * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
1150
+ * @param {Function} postSuccess Callback da chimare in caso di successo
1151
+ * @param {Function} postFailure Callback da chimare in caso di fallimento
1152
+ */
1153
+ manageCallResultResponse(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
1154
+ if (response.success) {
1155
+ if (response.haswarning)
1156
+ this.simpleWarning(response.warning);
1157
+ else if (successMsg)
1158
+ this.simpleSuccess(successMsg);
1159
+ if (postSuccess)
1160
+ postSuccess();
1161
+ }
1162
+ else {
1163
+ this.simpleError((errorMsg ? errorMsg + ": " : "") + response.haserror ? response.error : '[NO ERR.]');
1164
+ if (postFailure)
1165
+ postFailure();
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Metodo helper per gestire in maniera standardizzata una risposta di tipo **string**, considerando che:
1166
1170
  *
1167
- * Metodo interno richiamato da **setupForExport**
1171
+ * Stringa vuota --> Tutto bene
1168
1172
  *
1169
- * @param {any} obj Oggetto generico da esportare
1173
+ * Stringa piena --> Questo è il messaggio d'errore per cui la chiamata è fallita
1170
1174
  *
1171
- * @returns {CsvHeaders[]} Header di esportazione già ordinati correttamente
1175
+ * @param {string} response Risposta ricevuta dal server
1176
+ * @param {string} successMsg Messaggio da visualizzare in caso di successo
1177
+ * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
1178
+ * @param {Function} postSuccess Callback da chimare in caso di successo
1179
+ * @param {Function} postFailure Callback da chimare in caso di fallimento
1172
1180
  */
1173
- parseAndGetHeaders(obj, columnsSelection) {
1174
- var headers = [];
1175
- // Miracle incoming
1176
- let exportProps = obj[exportList] || [];
1177
- if (exportProps.length == 0)
1178
- console.warn("[@esfaenza/extensions] Nessuna colonna configurata da esportare!");
1179
- for (let i = 0; i < exportProps.length; i++) {
1180
- let prop = exportProps[i];
1181
- let propBase = "";
1182
- let toCall;
1183
- if (prop.endsWith(listCsvHdrPrefix)) {
1184
- propBase = prop.replace(listCsvHdrPrefix, '');
1185
- toCall = this.parseList.bind(this);
1186
- }
1187
- else if (prop.endsWith(dictionaryCsvHdrPrefix)) {
1188
- toCall = this.parseDictionary.bind(this);
1189
- propBase = prop.replace(dictionaryCsvHdrPrefix, '');
1190
- }
1191
- else if (prop.endsWith(csvVisPrefix)) {
1192
- propBase = prop.replace(csvVisPrefix, '');
1193
- if (typeof obj[propBase + csvValueNavigator] !== "undefined")
1194
- toCall = this.parseObject.bind(this);
1195
- else
1196
- toCall = this.parseProperty.bind(this);
1197
- }
1198
- if (toCall)
1199
- toCall(obj, propBase, headers, columnsSelection);
1181
+ manageStringResponse(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
1182
+ if (!response) {
1183
+ if (successMsg)
1184
+ this.simpleSuccess(successMsg);
1185
+ if (postSuccess)
1186
+ postSuccess();
1187
+ }
1188
+ else {
1189
+ if (errorMsg)
1190
+ this.simpleError(errorMsg + ": " + response);
1191
+ if (postFailure)
1192
+ postFailure();
1200
1193
  }
1201
- return headers;
1202
1194
  }
1203
1195
  /**
1204
- * Setup per l'esportazione di una singola proprietà.
1205
- * Viene generato l'header e la proprietà relativa al valore formattato o no
1196
+ * Metodo helper per gestire in maniera standardizzata una risposta di tipo **boolean**
1206
1197
  *
1207
- * @param {any} obj Oggetto di cui esportare la proprietà **prop**
1208
- * @param {string} prop Proprietà da esportare
1209
- * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
1198
+ * @param {string} response Risposta ricevuta dal server
1199
+ * @param {string} successMsg Messaggio da visualizzare in caso di successo
1200
+ * @param {string} errorMsg Messaggio da visualizzare in caso di fallimento
1201
+ * @param {Function} postSuccess Callback da chimare in caso di successo
1202
+ * @param {Function} postFailure Callback da chimare in caso di fallimento
1210
1203
  */
1211
- parseProperty(obj, prop, headers, columnsSelection) {
1212
- let toEmit = obj[prop + csvVisPrefix];
1213
- let format = obj[prop + csvFormatPrefix];
1214
- let propKey = obj[prop + csvPropKey];
1215
- if (propKey && !columnsSelection[propKey] && Object.keys(columnsSelection).length > 0) {
1216
- console.log("[@esfaenza/extensions] Colonna da non esportare: " + propKey);
1217
- return;
1204
+ manageStringResponseBool(response, successMsg, errorMsg, postSuccess = null, postFailure = null) {
1205
+ if (response) {
1206
+ if (successMsg)
1207
+ this.simpleSuccess(successMsg);
1208
+ if (postSuccess)
1209
+ postSuccess();
1218
1210
  }
1219
- let lbl = obj[prop + csvHdrPrefix];
1220
- let order = obj[prop + csvOrdPrefix];
1221
- if (toEmit && lbl !== "undefined") {
1222
- // Se non ho un formato esporto direttamente
1223
- if (!format) {
1224
- // Se è un formato data mi assicuro di non stampare mai una data 00:00:00 altrimenti in esportazione viene uno schifo
1225
- var val = obj[prop] ? this.dates.trimSeconds(obj[prop].toString()) : "";
1226
- Object.defineProperty(obj, prop + "_nofmt", { value: val, writable: false, enumerable: true });
1227
- headers.push({ label: lbl ? lbl : prop + "_nofmt", key: prop + "_nofmt", order: order, type: "string", propKey: propKey });
1228
- }
1229
- else {
1230
- // Caso formato numerico
1231
- if (format == "F") {
1232
- // Check considerando la possibilità di zeri
1233
- let val = obj[prop] != null ? obj[prop].toString() : "";
1234
- // Elimino tutte le virgole (indicatori di migliaia per la culture default en-US)
1235
- val = val.replace(/,/, "");
1236
- // Il singolo punto che c'è, se presente (separatore decimali) lo trasformo in virgola
1237
- let valConverted = val.replace(".", ",");
1238
- Object.defineProperty(obj, prop + "_fmt", { value: valConverted, writable: false, enumerable: true });
1239
- headers.push({ label: lbl ? lbl : prop + "_fmt", key: prop + "_fmt", order: order, type: "number", propKey: propKey });
1240
- }
1241
- else {
1242
- // Se ho un formato data trasformo in dayjs, formatto ed esporto
1243
- // Per la trasformazione in dayjs provo prima a passargli la stringa di netto, sperando la riconosca
1244
- // Se non funziona provo a definirgli la formattazione standard del metering per quel linguaggio
1245
- let val = this.dates.getFormatted(obj[prop], format.length <= 10, format.length > 16);
1246
- Object.defineProperty(obj, prop + "_fmt", { value: val ?? "", writable: false, enumerable: true });
1247
- headers.push({ label: lbl ? lbl : prop + "_fmt", key: prop + "_fmt", order: order, type: "date", propKey: propKey });
1248
- }
1249
- }
1211
+ else {
1212
+ if (errorMsg)
1213
+ this.simpleError(errorMsg);
1214
+ if (postFailure)
1215
+ postFailure();
1250
1216
  }
1251
1217
  }
1218
+ }
1219
+ MessageService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService, deps: [{ token: Injector }, { token: i1.BaseLocalization, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1220
+ MessageService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService, providedIn: "root" });
1221
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MessageService, decorators: [{
1222
+ type: Injectable,
1223
+ args: [{ providedIn: "root" }]
1224
+ }], ctorParameters: function () { return [{ type: i0.Injector, decorators: [{
1225
+ type: Inject,
1226
+ args: [Injector]
1227
+ }] }, { type: i1.BaseLocalization, decorators: [{
1228
+ type: Optional
1229
+ }] }]; } });
1230
+
1231
+ // Angular
1232
+ /**
1233
+ * Service che fornisce un accesso base in lettura/scrittura sulla clipboard
1234
+ */
1235
+ class ClipboardService {
1252
1236
  /**
1253
- * Setup per l'esportazione di una singola proprietà da un oggetto.
1254
- * Viene generato l'header e la proprietà relativa al valore formattato o no.
1237
+ * Costruttore
1255
1238
  *
1256
- * Per ottenere il valore e l'header della proprietà vengono utilizzati i navigatori auto-generati nel decoratore **ExportAs**
1239
+ * @ignore
1240
+ */
1241
+ constructor(msgService, locProvider) {
1242
+ this.msgService = msgService;
1243
+ this.locProvider = locProvider;
1244
+ /**
1245
+ * Oggetto statico di localizzazione per i messaggi di questa Service
1246
+ */
1247
+ this.loc = {
1248
+ "Unfortunately Firefox doesn't yet support direct Clipboard access if not via extensions, Please use Chrome to do this operation": {
1249
+ 'en-US': "Unfortunately Firefox doesn't yet support direct Clipboard access if not via extensions, Please use Chrome to do this operation",
1250
+ 'it-IT': "Mi dispiace ma Firefox non supporta l'accesso diretto alla Clipboard, se non attraverso le estensioni. Si prega di usare Chrome"
1251
+ },
1252
+ "Generic error during 'copy' command execution": {
1253
+ 'en-US': "Generic error during 'copy' command execution",
1254
+ 'it-IT': "Errore generico durante l'esecuzione del comando 'copy'"
1255
+ },
1256
+ "Error during Clipboard content read operation": {
1257
+ 'en-US': "Error during Clipboard content read operation",
1258
+ 'it-IT': "Errore nella lettura del contenuto della Clipboard"
1259
+ },
1260
+ "Error during Clipboard content copy operation": {
1261
+ 'en-US': "Error during Clipboard content copy operation",
1262
+ 'it-IT': "Errore nella scrittura del contenuto nella Clipboard"
1263
+ },
1264
+ "Please insert your clipboard content": {
1265
+ 'en-US': "Please insert your clipboard content",
1266
+ 'it-IT': "Si prega di incollare il contenuto della Clipboard"
1267
+ },
1268
+ "User input required": {
1269
+ 'en-US': "User input required",
1270
+ 'it-IT': "Richiesto Input utente"
1271
+ },
1272
+ "Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation": {
1273
+ 'en-US': "Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation",
1274
+ 'it-IT': "Il browser corrente non supporta la lettura diretta dei dati della Clipboard, si prega di effettuare un'incolla (CTRL + V) nella casella di testo e confermare l'operazione"
1275
+ },
1276
+ "Confirm": {
1277
+ 'en-US': "Confirm",
1278
+ 'it-IT': "Conferma"
1279
+ },
1280
+ "Cancel": {
1281
+ 'en-US': "Cancel",
1282
+ 'it-IT': "Annulla"
1283
+ },
1284
+ };
1285
+ }
1286
+ /**
1287
+ * Prende il valore della clipboard.
1288
+ * Si può specificare se ci si aspetta una lista o una matrice di valori in modo da ricevere già un risultato utilizzabile
1257
1289
  *
1258
- * @param {any} obj Oggetto di cui esportare la proprietà **prop**
1259
- * @param {string} prop Proprietà da esportare
1260
- * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
1290
+ * @param {'array' | 'matrix'} style Stile dell'output desiderato, se array o matrice
1291
+ * @param {Function} valueFoundCallback Callback che questa funzione chiamerà una volta ottenuto i valori richiesti dalla clipboard
1261
1292
  */
1262
- parseObject(obj, prop, headers) {
1263
- //se questa proprietà ha un navigator significa che deriva da un oggetto interno, recupero il valore in questa fase dato che prima non ce l'ho
1264
- let valuenavigator = obj[prop + csvValueNavigator];
1265
- //Es. di valuenavigator obj.subobj.subsubobj.property
1266
- let jmps = valuenavigator.split(".");
1267
- //Mi clono l'oggetto originale per non uccidergli i riferimenti
1268
- let clone = JSON.parse(JSON.stringify(obj));
1269
- //Salto ricorsivamente su me stesso per arrivare alla proprietà
1270
- //Salto 1 > diventa obj
1271
- //Salto 2 > diventa obj.subobj
1272
- //ecc...
1273
- jmps.forEach(pp => { clone = clone[pp]; });
1274
- //Valore finale :D
1275
- let value = clone;
1276
- let lbl = obj[prop + csvHdrPrefix];
1277
- let order = obj[prop + csvOrdPrefix];
1278
- //Se per questa proprietà ho un formato lo applico
1279
- let format = obj[prop + csvFormatPrefix];
1280
- let type = "string";
1281
- if (format == "F") {
1282
- let val = value.toString();
1283
- val = val.replace(/,/, "");
1284
- value = val.replace(".", ",");
1285
- type = "number";
1293
+ async getClipboard(style, valueFoundCallback) {
1294
+ let LOCALE = this.locProvider ? await this.locProvider.Locale.pipe(first()).toPromise() : 'it-IT';
1295
+ if (navigator.userAgent.indexOf("Firefox") != -1) {
1296
+ swal.fire({
1297
+ title: this.loc["User input required"][LOCALE],
1298
+ text: this.loc["Your current browser does not support reading from the clipboard directly. Please paste your clipboard's content in this Input field and confirm the operation"][LOCALE],
1299
+ input: 'textarea',
1300
+ icon: "warning",
1301
+ confirmButtonText: this.loc["Confirm"][LOCALE],
1302
+ cancelButtonText: this.loc["Cancel"][LOCALE],
1303
+ showCancelButton: true,
1304
+ customClass: {
1305
+ confirmButton: "btn btn-primary",
1306
+ cancelButton: "btn btn-secondary app-margin-right-15",
1307
+ container: "app-wrap",
1308
+ },
1309
+ reverseButtons: true,
1310
+ allowOutsideClick: false,
1311
+ inputValidator: (value) => {
1312
+ if (!value)
1313
+ return this.loc["Please insert your clipboard content"][LOCALE];
1314
+ }
1315
+ }).then(t => {
1316
+ if (t.isConfirmed) {
1317
+ console.log("[@esfaenza/extensions] Retrieving input data from user input");
1318
+ this.doClipboardDataGet(t.value, style, valueFoundCallback);
1319
+ }
1320
+ });
1321
+ return;
1286
1322
  }
1287
- else if (format) {
1288
- value = this.dates.getFormatted(value, format.length <= 10, format.length > 16);
1289
- type = "date";
1323
+ if (window.clipboardData) {
1324
+ console.log("[@esfaenza/extensions] Found clipboardData");
1325
+ let data = window.clipboardData.getData("Text");
1326
+ this.doClipboardDataGet(data, style, valueFoundCallback);
1327
+ }
1328
+ else if (window.navigator.clipboard) {
1329
+ console.log("[@esfaenza/extensions] Found navigator clipboard");
1330
+ window.navigator.permissions.query({ name: 'clipboard-read' }).then(result => {
1331
+ if (!(result.state === 'granted' || result.state === 'prompt')) {
1332
+ console.error("[@esfaenza/extensions] Clipboard permission not granted");
1333
+ if (valueFoundCallback)
1334
+ valueFoundCallback([]);
1335
+ return;
1336
+ }
1337
+ console.log("[@esfaenza/extensions] Clipboard permission granted, retrieving values");
1338
+ window.navigator.clipboard.readText()
1339
+ .then((t) => this.doClipboardDataGet(t, style, valueFoundCallback))
1340
+ .catch((err) => this.msgService.simpleError(this.loc["Error during Clipboard content read operation"][LOCALE] + ": " + err));
1341
+ });
1290
1342
  }
1291
- Object.defineProperty(obj, prop, { value: value ?? "", writable: false, enumerable: true });
1292
- headers.push({ label: lbl ? lbl : prop, key: prop, order: order, type: type, propKey: null });
1343
+ }
1344
+ /** @ignore */
1345
+ doClipboardDataGet(clipboardText, style, valueFoundCallback) {
1346
+ console.log("[@esfaenza/extensions] Retrieved clipboard values: " + clipboardText);
1347
+ let value = style == "array" ? this.getClipboardLines(clipboardText) :
1348
+ style == "matrix" ? this.getClipboardMatrix(clipboardText) : [];
1349
+ console.log(`[@esfaenza/extensions] Adapted for ${style}: ${JSON.stringify(value)}`);
1350
+ if (valueFoundCallback)
1351
+ valueFoundCallback(value);
1293
1352
  }
1294
1353
  /**
1295
- * Setup per l'esportazione di una lista di valori pivottati dinamicamente:
1296
- *
1297
- * => Recupero il valore utilizzando i navigatori
1298
- *
1299
- * => Recupero l'header utilizzando i navigatori
1300
- *
1301
- * => Con il valore ci creo la property
1354
+ * Ottiene il contenuto della clipboard diviso per righe (separate da newline)
1302
1355
  *
1303
- * => Con l'header e il riferimento alla property appena creata creo un record negli header da esportare
1356
+ * @param {string} data contenuto letto dalla clipboard
1304
1357
  *
1305
- * @param {any} obj Oggetto di cui esportare la proprietà **prop**
1306
- * @param {string} prop Proprietà da esportare
1307
- * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
1358
+ * @returns {string[]} Array contenente il testo preso dalla clipboard diviso per riga
1308
1359
  */
1309
- parseList(obj, prop, headers) {
1310
- let headernavigator = obj[prop + listCsvHdrPrefix];
1311
- let valuenavigator = obj[prop + listCsvValPrefix];
1312
- let valueformat = obj[prop + listCsvValFormat];
1313
- let order = obj[prop + csvOrdPrefix];
1314
- var jumps, tmpRes;
1315
- var objs = obj[prop];
1316
- for (let idx = 0; idx < objs.length; idx++) {
1317
- let listitem = objs[idx];
1318
- var headersplit = headernavigator.split(" ");
1319
- var header = "";
1320
- // Per l'header si considera a se stante ogni pezzo separato da spazio per fare le concatenazioni
1321
- headersplit.forEach(pr => {
1322
- jumps = pr.split(".");
1323
- tmpRes = JSON.parse(JSON.stringify(listitem));
1324
- jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
1325
- if (!header)
1326
- header = tmpRes;
1327
- else
1328
- header += " " + tmpRes;
1329
- });
1330
- jumps = valuenavigator.split(".");
1331
- tmpRes = JSON.parse(JSON.stringify(listitem));
1332
- jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
1333
- let value = tmpRes;
1334
- let type = "string";
1335
- if (valueformat == "F") {
1336
- let val = value != null ? value.toString() : "";
1337
- val = val.replace(/,/, "");
1338
- value = val.replace(".", ",");
1339
- type = "number";
1340
- }
1341
- else if (valueformat) {
1342
- value = this.dates.getFormatted(obj[prop], valueformat.length <= 10, valueformat.length > 16);
1343
- type = "date";
1344
- }
1345
- Object.defineProperty(obj, prop + idx, { value: value ?? "", writable: false });
1346
- headers.push({ label: header, key: prop + idx, order: order + idx, type: type, propKey: null });
1347
- }
1360
+ getClipboardLines(data) {
1361
+ let trimmedData = data.trim();
1362
+ if (!trimmedData.includes("\r\n") && trimmedData.includes("\n"))
1363
+ return trimmedData.split("\n");
1364
+ return trimmedData.split("\r\n");
1348
1365
  }
1349
1366
  /**
1350
- * Setup per l'esportazione di una lista di valori pivottati dinamicamente:
1351
- *
1352
- * => Recupero il valore utilizzando i navigatori
1353
- *
1354
- * => Recupero l'header utilizzando i navigatori
1355
- *
1356
- * => Con il valore ci creo la property
1367
+ * Ottiene il contenuto della clipboard diviso per righe (separate da newline) e colonne (separate da tab)
1357
1368
  *
1358
- * => Con l'header e il riferimento alla property appena creata creo un record negli header da esportare
1369
+ * @param {string} data contenuto letto dalla clipboard
1359
1370
  *
1360
- * @param {any} obj Oggetto di cui esportare la proprietà **prop**
1361
- * @param {string} prop Proprietà da esportare
1362
- * @param {CsvHeaders} headers Contenitore degli header, verrà riempito da questa funzione
1371
+ * @returns {string[][]} Matrice contenente il testo preso dalla clipboard diviso per riga e colonna
1363
1372
  */
1364
- parseDictionary(obj, prop, headers) {
1365
- let headernavigator = obj[prop + dictionaryCsvHdrPrefix];
1366
- let valuenavigator = obj[prop + dictionaryCsvValPrefix];
1367
- let order = obj[prop + csvOrdPrefix];
1368
- var jumps, tmpRes;
1369
- var objs = obj[prop];
1370
- for (let idx = 0; idx < objs.length; idx++) {
1371
- let listitem = objs[idx];
1372
- var headersplit = headernavigator.split(" ");
1373
- var header = "";
1374
- headersplit.forEach(pr => {
1375
- jumps = pr.split(".");
1376
- tmpRes = JSON.parse(JSON.stringify(listitem));
1377
- jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
1378
- if (!header)
1379
- header = tmpRes;
1380
- else
1381
- header += " " + tmpRes;
1382
- });
1383
- var valuesplit = valuenavigator.split(",");
1384
- valuesplit.forEach((t, idxinternal) => {
1385
- var navig = t.split("-")[1];
1386
- var desc = t.split("-")[0];
1387
- jumps = navig.split(".");
1388
- tmpRes = JSON.parse(JSON.stringify(listitem));
1389
- jumps.forEach(pp => { tmpRes = tmpRes[pp]; });
1390
- let value = tmpRes;
1391
- let propname = prop + idx.toString() + idxinternal.toString();
1392
- Object.defineProperty(obj, propname, { value: value ?? "", writable: false });
1393
- headers.push({ label: header + " " + desc, key: propname, order: order + idx, type: 'string', propKey: null });
1394
- });
1395
- }
1373
+ getClipboardMatrix(data) {
1374
+ var ret = [];
1375
+ var rows = !data.includes("\r\n") && data.includes("\n") ? data.split("\n") : data.split("\r\n");
1376
+ // A quanto pare ogni valore è separato da un \t... spero che sia uno standard di quando si copia da excel altrimenti ciao
1377
+ for (let i = 0; i < rows.length; i++)
1378
+ ret.push(rows[i].split('\t').map(t => t.trim()));
1379
+ return ret;
1396
1380
  }
1397
1381
  /**
1398
- * Metodo che si occupa della deserializzazione degli oggetti da esportare. Messo a livello di libreria in modo che qualora ci fosse da cambiare libreria
1399
- * tutte le manutenzioni siano concentrate qui
1400
- *
1401
- * @param {any[]} items Oggetti da deserializzare
1402
- * @param {any} type Tipo da usare come prototipo per la deserializzazione
1382
+ * Metodo che copia un testo all'interno della clipboard
1403
1383
  *
1404
- * @returns {any} Oggetti deserializzati
1384
+ * @param {string} text Testo da inserire nella clipboard
1405
1385
  */
1406
- deserializeForExport(items, type) {
1407
- return Deserialize(items, type);
1386
+ async copyTextToClipboard(text) {
1387
+ let LOCALE = this.locProvider ? await this.locProvider.Locale.pipe(first()).toPromise() : 'it-IT';
1388
+ var textArea = document.createElement("textarea");
1389
+ //
1390
+ // *** This styling is an extra step which is likely not required. ***
1391
+ //
1392
+ // Why is it here? To ensure:
1393
+ // 1. the element is able to have focus and selection.
1394
+ // 2. if element was to flash render it has minimal visual impact.
1395
+ // 3. less flakyness with selection and copying which **might** occur if
1396
+ // the textarea element is not visible.
1397
+ //
1398
+ // The likelihood is the element won't even render, not even a flash,
1399
+ // so some of these are just precautions. However in IE the element
1400
+ // is visible whilst the popup box asking the user for permission for
1401
+ // the web page to copy to the clipboard.
1402
+ //
1403
+ // Place in top-left corner of screen regardless of scroll position.
1404
+ textArea.style.position = "fixed";
1405
+ textArea.style.top = "0";
1406
+ textArea.style.left = "0";
1407
+ // Ensure it has a small width and height. Setting to 1px / 1em
1408
+ // doesn't work as this gives a negative w/h on some browsers.
1409
+ textArea.style.width = "2em";
1410
+ textArea.style.height = "2em";
1411
+ // We don't need padding, reducing the size if it does flash render.
1412
+ textArea.style.padding = "0";
1413
+ // Clean up any borders.
1414
+ textArea.style.border = "none";
1415
+ textArea.style.outline = "none";
1416
+ textArea.style.boxShadow = "none";
1417
+ // Avoid flash of white box if rendered for any reason.
1418
+ textArea.style.background = "transparent";
1419
+ textArea.value = text;
1420
+ document.body.appendChild(textArea);
1421
+ textArea.focus();
1422
+ textArea.select();
1423
+ try {
1424
+ var successful = document.execCommand("copy");
1425
+ if (!successful)
1426
+ this.msgService.simpleError(this.loc["Generic error during 'copy' command execution"][LOCALE]);
1427
+ }
1428
+ catch (err) {
1429
+ this.msgService.simpleError(this.loc["Error during Clipboard content copy operation"][LOCALE] + ": " + err);
1430
+ }
1431
+ document.body.removeChild(textArea);
1408
1432
  }
1409
1433
  }
1410
- ExportService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService, deps: [{ token: DateService }], target: i0.ɵɵFactoryTarget.Injectable });
1411
- ExportService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService });
1412
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ExportService, decorators: [{
1413
- type: Injectable
1414
- }], ctorParameters: function () { return [{ type: DateService }]; } });
1434
+ ClipboardService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService, deps: [{ token: MessageService }, { token: i1.BaseLocalization, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1435
+ ClipboardService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService, providedIn: "root" });
1436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ClipboardService, decorators: [{
1437
+ type: Injectable,
1438
+ args: [{ providedIn: "root" }]
1439
+ }], ctorParameters: function () { return [{ type: MessageService }, { type: i1.BaseLocalization, decorators: [{
1440
+ type: Optional
1441
+ }] }]; } });
1415
1442
 
1416
1443
  // Angular
1417
1444
  /**
@@ -1715,9 +1742,10 @@ class HashingService {
1715
1742
  }
1716
1743
  }
1717
1744
  HashingService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HashingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1718
- HashingService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HashingService });
1745
+ HashingService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HashingService, providedIn: "root" });
1719
1746
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HashingService, decorators: [{
1720
- type: Injectable
1747
+ type: Injectable,
1748
+ args: [{ providedIn: "root" }]
1721
1749
  }] });
1722
1750
 
1723
1751
  /**
@@ -1966,11 +1994,37 @@ class UtilityService {
1966
1994
  }
1967
1995
  }
1968
1996
  UtilityService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UtilityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1969
- UtilityService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UtilityService });
1997
+ UtilityService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UtilityService, providedIn: "root" });
1970
1998
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UtilityService, decorators: [{
1971
- type: Injectable
1999
+ type: Injectable,
2000
+ args: [{ providedIn: "root" }]
1972
2001
  }] });
1973
2002
 
2003
+ /**
2004
+ * Classe che rappresenta una risposta ricevuta da un Backend
2005
+ */
2006
+ class CallResult {
2007
+ }
2008
+
2009
+ /**
2010
+ * Header per il file CSV che sta venendo generato
2011
+ */
2012
+ class CsvHeaders {
2013
+ }
2014
+
2015
+ /**
2016
+ * Rappresentazione di un oggetto generico
2017
+ */
2018
+ class GenericItem {
2019
+ }
2020
+
2021
+ /** Enumeratore dei tipi di messaggio che i moduli esterni possono contattare sull'applicazione principale */
2022
+ var InboundMessageTypes;
2023
+ (function (InboundMessageTypes) {
2024
+ /** Evento di navigazione */
2025
+ InboundMessageTypes[InboundMessageTypes["Navigation"] = 0] = "Navigation";
2026
+ })(InboundMessageTypes || (InboundMessageTypes = {}));
2027
+
1974
2028
  /** Classe che rappresenta un generico messaggio scambiato fra applicazione e moduli */
1975
2029
  class InterComMessage {
1976
2030
  /**
@@ -2027,76 +2081,12 @@ class InterComService {
2027
2081
  }
2028
2082
  }
2029
2083
  InterComService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: InterComService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2030
- InterComService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: InterComService });
2084
+ InterComService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: InterComService, providedIn: "root" });
2031
2085
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: InterComService, decorators: [{
2032
- type: Injectable
2033
- }] });
2034
-
2035
- // Angular
2036
- /**
2037
- * Modulo di estensione che registra autonomamente **TUTTI** i servizi di estensione:
2038
- * ClipboardService,
2039
- * DateService,
2040
- * ExportService,
2041
- * HashingService,
2042
- * MessageService,
2043
- * UtilityService
2044
- * InterComService
2045
- *
2046
- * con eventuali provider che utilizzano
2047
- */
2048
- class FullExtensionsModule {
2049
- static forRoot(config) {
2050
- return {
2051
- ngModule: FullExtensionsModule,
2052
- providers: [
2053
- ClipboardService,
2054
- DateService,
2055
- ExportService,
2056
- HashingService,
2057
- MessageService,
2058
- UtilityService,
2059
- InterComService,
2060
- { provide: EXT_LOCALE, useValue: config?.locale || 'it-IT' },
2061
- { provide: EXT_FANCY_ALERTS, useValue: config?.fancy_alerts || true },
2062
- { provide: APPSEARCH_PREFIX, useValue: config?.appsearch_prefix || 'Hot' },
2063
- { provide: EXT_ALLOW_UTC, useValue: config?.allow_utc == null ? false : config?.allow_utc }
2064
- ]
2065
- };
2066
- }
2067
- }
2068
- FullExtensionsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FullExtensionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2069
- FullExtensionsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: FullExtensionsModule });
2070
- FullExtensionsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FullExtensionsModule });
2071
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FullExtensionsModule, decorators: [{
2072
- type: NgModule
2086
+ type: Injectable,
2087
+ args: [{ providedIn: "root" }]
2073
2088
  }] });
2074
2089
 
2075
- /**
2076
- * Classe che rappresenta una risposta ricevuta da un Backend
2077
- */
2078
- class CallResult {
2079
- }
2080
-
2081
- /**
2082
- * Header per il file CSV che sta venendo generato
2083
- */
2084
- class CsvHeaders {
2085
- }
2086
-
2087
- /**
2088
- * Rappresentazione di un oggetto generico
2089
- */
2090
- class GenericItem {
2091
- }
2092
-
2093
- /** Enumeratore dei tipi di messaggio che i moduli esterni possono contattare sull'applicazione principale */
2094
- var InboundMessageTypes;
2095
- (function (InboundMessageTypes) {
2096
- /** Evento di navigazione */
2097
- InboundMessageTypes[InboundMessageTypes["Navigation"] = 0] = "Navigation";
2098
- })(InboundMessageTypes || (InboundMessageTypes = {}));
2099
-
2100
2090
  /*
2101
2091
  * Public API Surface of extensions
2102
2092
  */
@@ -2105,5 +2095,5 @@ var InboundMessageTypes;
2105
2095
  * Generated bundle index. Do not edit.
2106
2096
  */
2107
2097
 
2108
- export { APPSEARCH_PREFIX, CallResult, ClipboardService, CsvHeaders, DateService, EXT_ALLOW_UTC, EXT_FANCY_ALERTS, EXT_LOCALE, Export, ExportAs, ExportDictionary, ExportList, ExportService, ExtensionsModule, FullExtensionsModule, GenericItem, HashingService, InboundMessageTypes, InterComMessage, InterComService, MessageService, UtilityService };
2098
+ export { APPSEARCH_PREFIX, CallResult, ClipboardService, CsvHeaders, DateService, EXT_ALLOW_UTC, Export, ExportAs, ExportDictionary, ExportList, ExportService, ExtensionsModule, GenericItem, HashingService, InboundMessageTypes, InterComMessage, InterComService, LocDatePipe, MessageService, UtilityService };
2109
2099
  //# sourceMappingURL=esfaenza-extensions.mjs.map