@osimatic/helpers-js 1.2.7 → 1.3.0

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.
package/chartjs.js ADDED
@@ -0,0 +1,309 @@
1
+ class Chartjs {
2
+ static init() {
3
+ if (typeof Chartjs.initialized == 'undefined' || Chartjs.initialized) {
4
+ return;
5
+ }
6
+
7
+ Chartjs.initialized = true;
8
+
9
+ const centerTextPlugin = {
10
+ id: 'centerText',
11
+ afterDraw(chart) {
12
+ if (typeof chart.config.options.centerText == 'undefined' || !chart.config.options.centerText.display) {
13
+ return;
14
+ }
15
+
16
+ const { ctx, chartArea: { width, height } } = chart;
17
+ ctx.save();
18
+ const total = chart.data.datasets[0].data.reduce((a,b) => a+b, 0);
19
+ const label = chart.config.options.centerText.label || '';
20
+ const fontSize = chart.config.options.centerText.fontSize || 16;
21
+ const textColor = chart.config.options.centerText.color || '#333';
22
+
23
+ ctx.font = `bold ${fontSize}px sans-serif`;
24
+ ctx.fillStyle = textColor;
25
+ ctx.textAlign = 'center';
26
+ ctx.textBaseline = 'middle';
27
+ ctx.fillText(`${total}`, width / 2, height / 2);
28
+ if (label) {
29
+ ctx.font = `12px sans-serif`;
30
+ ctx.fillText(label, width / 2, height / 2 + 20);
31
+ }
32
+ ctx.restore();
33
+ }
34
+ };
35
+
36
+ Chart.register(centerTextPlugin);
37
+ }
38
+
39
+ static createStackedChart(chartDiv, chartData, title=null, options={}) {
40
+ chartDiv.empty();
41
+ new Chart(chartDiv.get(0).getContext("2d"), $.extend(true, {}, {
42
+ type: "bar",
43
+ data: {
44
+ labels: chartData.labels,
45
+ datasets: chartData.datasets
46
+ },
47
+ options: {
48
+ responsive: true,
49
+ scales: {
50
+ x: {
51
+ stacked: true
52
+ },
53
+ y: {
54
+ stacked: true,
55
+ beginAtZero: true,
56
+ ticks: {
57
+ precision: 0
58
+ },
59
+ }
60
+ },
61
+ plugins: {
62
+ title: {
63
+ display: null !== title,
64
+ text: title || '',
65
+ font: { size: 14 },
66
+ color: "#333",
67
+ padding: { top: 10, bottom: 20 }
68
+ },
69
+ legend: {
70
+ position: "bottom",
71
+ labels: {
72
+ usePointStyle: true,
73
+ padding: 15
74
+ }
75
+ },
76
+ tooltip: {
77
+ callbacks: {
78
+ label: function(context) {
79
+ return `${context.dataset.label}: ${context.parsed.y}`;
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }, options));
86
+ }
87
+
88
+ static createBarChart(chartDiv, chartData, title=null, options={}) {
89
+ chartDiv.empty();
90
+ new Chart(chartDiv.get(0).getContext("2d"), $.extend(true, {}, {
91
+ type: "bar",
92
+ data: {
93
+ labels: chartData.labels,
94
+ datasets: chartData.datasets
95
+ },
96
+ options: {
97
+ responsive: false,
98
+ maintainAspectRatio: false,
99
+ aspectRatio: 2,
100
+ scales: {
101
+ x: {
102
+ grid: {
103
+ display: false
104
+ },
105
+ /*ticks: {
106
+ font: { size: 12 }
107
+ }*/
108
+ },
109
+ y: {
110
+ beginAtZero: true,
111
+ ticks: {
112
+ precision: 0
113
+ //stepSize: 10, font: { size: 12 }
114
+ },
115
+ grid: {
116
+ color: "#eee"
117
+ }
118
+ }
119
+ },
120
+ plugins: {
121
+ title: {
122
+ display: null !== title,
123
+ text: title || '',
124
+ font: { size: 14 },
125
+ color: "#333",
126
+ padding: { top: 10, bottom: 20 }
127
+ },
128
+ legend: {
129
+ display: chartData.datasets.length > 1,
130
+ position: "bottom",
131
+ },
132
+ tooltip: {
133
+ callbacks: {
134
+ label: context => context.dataset.label + ' : ' + context.parsed.y
135
+ //label: (context) => `${context.formattedValue} pointages`
136
+ }
137
+ }
138
+ },
139
+ animation: {
140
+ duration: 1200,
141
+ easing: "easeOutQuart"
142
+ }
143
+ }
144
+ }, options));
145
+ }
146
+
147
+ static createLineChart(chartDiv, chartData, title=null, options={}) {
148
+ Chartjs.init();
149
+
150
+ chartDiv.empty();
151
+ new Chart(chartDiv.get(0).getContext("2d"), $.extend(true, {}, {
152
+ type: "line",
153
+ data: {
154
+ labels: chartData.labels,
155
+ datasets: chartData.datasets
156
+ },
157
+ options: {
158
+ responsive: false,
159
+ maintainAspectRatio: false,
160
+ aspectRatio: 2,
161
+ scales: {
162
+ y: {
163
+ beginAtZero: true,
164
+ ticks: {
165
+ precision: 0
166
+ },
167
+ grid: {
168
+ color: "#eee"
169
+ }
170
+ },
171
+ x: {
172
+ grid: {
173
+ display: false
174
+ }
175
+ }
176
+ },
177
+ plugins: {
178
+ title: {
179
+ display: null !== title,
180
+ text: title || '',
181
+ font: { size: 14 },
182
+ color: "#333",
183
+ padding: { top: 10, bottom: 20 }
184
+ },
185
+ legend: {
186
+ display: chartData.datasets.length > 1,
187
+ position: "bottom",
188
+ },
189
+ tooltip: {
190
+ callbacks: {
191
+ label: context => context.dataset.label + ' : ' + context.parsed.y
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }, options));
197
+ }
198
+
199
+ static createDoughnutChart(chartDiv, chartData, title=null, options={}) {
200
+ Chartjs.init();
201
+
202
+ chartDiv.empty();
203
+ new Chart(chartDiv.get(0).getContext("2d"), $.extend(true, {}, {
204
+ type: "doughnut",
205
+ data: {
206
+ labels: chartData.labels,
207
+ datasets: [{
208
+ data: chartData.values,
209
+ backgroundColor: chartData.colors,
210
+ borderWidth: 0,
211
+ hoverOffset: 10
212
+ }]
213
+ },
214
+ options: {
215
+ cutout: "65%",
216
+ responsive: false,
217
+ maintainAspectRatio: false,
218
+ aspectRatio: 2,
219
+ plugins: {
220
+ title: {
221
+ display: null !== title,
222
+ text: title || ''
223
+ },
224
+ legend: {
225
+ position: "right",
226
+ labels: {
227
+ boxWidth: 12,
228
+ font: { size: 12 },
229
+ usePointStyle: true
230
+ }
231
+ },
232
+ tooltip: {
233
+ callbacks: {
234
+ label: function(context) {
235
+ const total = context.dataset.data.reduce((a,b)=>a+b,0);
236
+ const value = context.raw;
237
+ const percent = ((value / total) * 100).toFixed(1);
238
+ return `${context.label}: ${value} (${percent}%)`;
239
+ }
240
+ }
241
+ }
242
+ },
243
+ animation: {
244
+ animateRotate: true,
245
+ animateScale: true
246
+ },
247
+ centerText: {
248
+ display: false,
249
+ fontSize: 18,
250
+ color: "#000"
251
+ }
252
+ }
253
+ }, options));
254
+ }
255
+
256
+ static groupByPeriod(data, period, metrics) {
257
+ const grouped = {};
258
+
259
+ //data = Object.entries(dataObj).map(([date, values]) => ({ date, ...values }));
260
+
261
+ Object.entries(data).forEach(([date, values]) => {
262
+ //data.forEach(entry => {
263
+ const d = new Date(date);
264
+ let key;
265
+
266
+ if (period === 'week') {
267
+ const week = Math.ceil((d.getDate() - d.getDay() + 1) / 7);
268
+ key = `${d.getFullYear()}-S${week}`;
269
+ }
270
+ else if (period === 'month') {
271
+ key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
272
+ }
273
+ else {
274
+ key = date;
275
+ }
276
+
277
+ if (!grouped[key]) {
278
+ grouped[key] = {};
279
+ metrics.forEach(m => grouped[key][m] = []);
280
+ }
281
+
282
+ metrics.forEach(m => {
283
+ if (values[m] !== undefined) grouped[key][m].push(values[m]);
284
+ });
285
+ });
286
+
287
+ return Object.entries(grouped).map(([label, vals]) => {
288
+ const aggregated = {};
289
+ metrics.forEach(m => {
290
+ aggregated[m] = vals[m].reduce((a, b) => a + b, 0) / vals[m].length;
291
+ });
292
+ return { label, ...aggregated };
293
+ });
294
+ }
295
+
296
+ static getPeriodLabels(data, period, locale = 'fr-FR', timeZone = 'Europe/Paris') {
297
+ return DatePeriod.getPeriodLabels(Object.keys(data), period, locale, timeZone);
298
+ }
299
+
300
+ static getAutoGranularity(data) {
301
+ const dates = Object.keys(data);
302
+ const days = (new Date(dates[dates.length - 1]) - new Date(dates[0])) / (1000 * 60 * 60 * 24);
303
+ if (days > 90) return 'month';
304
+ if (days > 30) return 'week';
305
+ return 'day_of_month';
306
+ }
307
+ }
308
+
309
+ module.exports = { Chartjs };
package/date_time.js CHANGED
@@ -121,8 +121,8 @@ class DateTime {
121
121
 
122
122
  static getTimeDisplayWithNbDays(jsDate, jsPreviousDate, locale="fr-FR", timeZone="Europe/Paris") {
123
123
  let str = this.getTimeDisplay(jsDate, locale, timeZone);
124
- if (jsPreviousDate != 0 && jsPreviousDate != null) {
125
- let nbDaysDiff = this.getNbDayBetweenTwo(jsPreviousDate, jsDate, false);
124
+ if (jsPreviousDate !== 0 && jsPreviousDate != null) {
125
+ let nbDaysDiff = DatePeriod.getNbDayBetweenTwo(jsPreviousDate, jsDate, false);
126
126
  if (nbDaysDiff > 0) {
127
127
  str += ' (J+'+nbDaysDiff+')';
128
128
  }
@@ -238,7 +238,7 @@ class DateTime {
238
238
  }
239
239
 
240
240
  static isDateEqual(jsDate1, jsDate2) {
241
- return (jsDate1.getFullYear() == jsDate2.getFullYear() && jsDate1.getMonth() == jsDate2.getMonth() && jsDate1.getDate() == jsDate2.getDate());
241
+ return (jsDate1.getFullYear() === jsDate2.getFullYear() && jsDate1.getMonth() === jsDate2.getMonth() && jsDate1.getDate() === jsDate2.getDate());
242
242
  }
243
243
 
244
244
  static isDateInThePast(jsDate) {
@@ -277,6 +277,27 @@ class DateTime {
277
277
  return jsDateTime > today;
278
278
  }
279
279
 
280
+ static addDays(date, days) {
281
+ date.setUTCDate(date.getUTCDate() + days);
282
+ return date;
283
+ }
284
+
285
+ static addMonths(date, months) {
286
+ let d = date.getDate();
287
+ date.setMonth(date.getMonth() + +months);
288
+ if (date.getDate() !== d) {
289
+ date.setDate(0);
290
+ }
291
+ return date;
292
+ }
293
+
294
+ /** @deprecated use DatePeriod.getNbDayBetweenTwo instead */
295
+ static getNbDayBetweenTwo(jsDate1, jsDate2, asPeriod=false, timeZone="Europe/Paris") {
296
+ return DatePeriod.getNbDayBetweenTwo(jsDate1, jsDate2, asPeriod, timeZone);
297
+ }
298
+ }
299
+
300
+ class DatePeriod {
280
301
  static getNbDayBetweenTwo(jsDate1, jsDate2, asPeriod=false, timeZone="Europe/Paris") {
281
302
  //jsDate1.set
282
303
  if (jsDate1 == null || jsDate2 == null) {
@@ -299,18 +320,34 @@ class DateTime {
299
320
  return parseInt(Math.round((timestamp2-timestamp1)/86400));
300
321
  }
301
322
 
302
- static addDays(date, days) {
303
- date.setUTCDate(date.getUTCDate() + days);
304
- return date;
305
- }
323
+ static getPeriodLabels(data, period, locale = 'fr-FR', timeZone = 'Europe/Paris') {
324
+ if (!data || data.length === 0) {
325
+ return [];
326
+ }
306
327
 
307
- static addMonths(date, months) {
308
- let d = date.getDate();
309
- date.setMonth(date.getMonth() + +months);
310
- if (date.getDate() !== d) {
311
- date.setDate(0);
328
+ if (period === 'month') {
329
+ data.map(yearAndMonth => {
330
+ const [year, month] = yearAndMonth.split('-').map(Number);
331
+ return DateTime.getMonthNameByMonth(month, locale).capitalize()+' '+year;
332
+ });
312
333
  }
313
- return date;
334
+ if (period === 'week') {
335
+ return data.map(yearAndWeek => {
336
+ const [year, weekStr] = yearAndWeek.split('-S');
337
+ const week = parseInt(weekStr, 10);
338
+ return 'S'+week+' '+year;
339
+ });
340
+ }
341
+ if (period === 'day_of_week') {
342
+ return data.map(weekDay => DateTime.getDayNameByDayOfWeek(weekDay, locale).capitalize());
343
+ }
344
+ if (period === 'day_of_month') {
345
+ return data.map(sqlDate => SqlDate.getDateDigitalDisplay(sqlDate, locale, timeZone));
346
+ }
347
+ if (period === 'hour') {
348
+ return data.map(hour => String(hour).padStart(2, '0')+'h');
349
+ }
350
+ return data;
314
351
  }
315
352
  }
316
353
 
@@ -386,7 +423,7 @@ class TimestampUnix {
386
423
  }
387
424
 
388
425
  static getNbDayBetweenTwo(timestamp1, timestamp2, asPeriod=false, timeZone="Europe/Paris") {
389
- return DateTime.getNbDayBetweenTwo(this.parse(timestamp1), this.parse(timestamp2), asPeriod, timeZone);
426
+ return DatePeriod.getNbDayBetweenTwo(this.parse(timestamp1), this.parse(timestamp2), asPeriod, timeZone);
390
427
  }
391
428
 
392
429
  static isDateInThePast(timestamp) {
@@ -452,7 +489,7 @@ class SqlDate {
452
489
  return DateTime.isDateInTheFuture(SqlDateTime.parse(sqlDate + " 00:00:00"));
453
490
  }
454
491
  static getNbDayBetweenTwo(sqlDate1, sqlDate2, asPeriod=false) {
455
- return DateTime.getNbDayBetweenTwo(SqlDateTime.parse(sqlDate1 + " 00:00:00"), SqlDateTime.parse(sqlDate2 + " 00:00:00"), asPeriod);
492
+ return DatePeriod.getNbDayBetweenTwo(SqlDateTime.parse(sqlDate1 + " 00:00:00"), SqlDateTime.parse(sqlDate2 + " 00:00:00"), asPeriod);
456
493
  }
457
494
  }
458
495
 
@@ -465,7 +502,7 @@ class SqlTime {
465
502
  return null;
466
503
  }
467
504
 
468
- if ((sqlTime.match(/\:/g) || []).length == 1) {
505
+ if ((sqlTime.match(/\:/g) || []).length === 1) {
469
506
  sqlTime += ':00';
470
507
  }
471
508
 
@@ -585,9 +622,9 @@ class SqlDateTime {
585
622
  }
586
623
 
587
624
  static getNbDayBetweenTwo(sqlDateTime1, sqlDateTime2, asPeriod=false) {
588
- return DateTime.getNbDayBetweenTwo(this.parse(sqlDateTime1), this.parse(sqlDateTime2), asPeriod);
625
+ return DatePeriod.getNbDayBetweenTwo(this.parse(sqlDateTime1), this.parse(sqlDateTime2), asPeriod);
589
626
  }
590
627
 
591
628
  }
592
629
 
593
- module.exports = { DateTime, TimestampUnix, SqlDate, SqlTime, SqlDateTime };
630
+ module.exports = { DateTime, DatePeriod, TimestampUnix, SqlDate, SqlTime, SqlDateTime };
package/duration.js CHANGED
@@ -51,30 +51,31 @@ class Duration {
51
51
  return (durationInSecondsOriginal < 0 ? '- ' : '')+hours+':'+minutes+(displayMode==='input_time'?':':'.')+seconds;
52
52
  }
53
53
 
54
- static convertToDurationInHourStringDisplay(durationInSeconds, withSecondes=true, withMinutes=true, withLibelleMinute=true, libelleEntier=false) {
54
+ static convertToDurationInHourStringDisplay(durationInSeconds, withSeconds=true, withMinutes=true, withMinuteLabel=true, fullLabel=false, hideHourIfZeroHour=false) {
55
55
  durationInSeconds = Math.round(durationInSeconds);
56
56
 
57
57
  // Heures
58
58
  let strHeure = '';
59
59
  let nbHeures = this.getNbHoursOfDurationInSeconds(durationInSeconds);
60
- strHeure += nbHeures;
61
- if (libelleEntier) {
62
- strHeure += ' heure'+(nbHeures>1?'s':'');
63
- }
64
- else {
65
- strHeure += 'h';
60
+ if (!hideHourIfZeroHour || nbHeures > 0) {
61
+ strHeure += nbHeures;
62
+ if (fullLabel) {
63
+ strHeure += ' heure'+(nbHeures>1?'s':'');
64
+ }
65
+ else {
66
+ strHeure += 'h';
67
+ }
66
68
  }
67
69
 
68
70
  // Minutes
69
71
  let strMinute = '';
70
- let nbMinutes = 0;
71
72
  if (withMinutes) {
72
- nbMinutes = this.getNbMinutesRemainingOfDurationInSeconds(durationInSeconds);
73
+ let nbMinutes = this.getNbMinutesRemainingOfDurationInSeconds(durationInSeconds);
73
74
  strMinute += ' ';
74
75
  //strMinute += sprintf('%02d', nbMinutes);
75
76
  strMinute += nbMinutes.toString().padStart(2, '0');
76
- if (withLibelleMinute) {
77
- if (libelleEntier) {
77
+ if (withMinuteLabel) {
78
+ if (fullLabel) {
78
79
  strMinute += ' minute'+(nbMinutes>1?'s':'');
79
80
  }
80
81
  else {
@@ -85,12 +86,12 @@ class Duration {
85
86
 
86
87
  // Secondes
87
88
  let strSeconde = '';
88
- if (withSecondes) {
89
+ if (withSeconds) {
89
90
  let nbSecondes = this.getNbSecondsRemainingOfDurationInSeconds(durationInSeconds);
90
91
  strSeconde += ' ';
91
92
  //strSeconde += sprintf('%02d', nbSecondes);
92
93
  strSeconde += nbSecondes.toString().padStart(2, '0');
93
- if (libelleEntier) {
94
+ if (fullLabel) {
94
95
  strSeconde += ' seconde'+(nbSecondes>1?'s':'');
95
96
  }
96
97
  else {
package/index.js CHANGED
@@ -12,7 +12,7 @@ const { HTTPRequest, Cookie, UrlAndQueryString } = require('./network');
12
12
  const { IBAN, BankCard } = require('./bank');
13
13
  const { AudioMedia, VideoMedia, UserMedia } = require('./media');
14
14
  const { PersonName, Email, TelephoneNumber } = require('./contact_details');
15
- const { DateTime, TimestampUnix, SqlDate, SqlTime, SqlDateTime } = require('./date_time');
15
+ const { DateTime, DatePeriod, TimestampUnix, SqlDate, SqlTime, SqlDateTime } = require('./date_time');
16
16
  const { Duration } = require('./duration');
17
17
  const { File, CSV, Img } = require('./file');
18
18
  const { FormHelper, EditValue } = require('./form_helper');
@@ -41,6 +41,7 @@ const { WebRTC } = require('./web_rtc');
41
41
  const { EventBus } = require('./event_bus');
42
42
 
43
43
  // exports surcouche lib externe
44
+ const { Chartjs } = require('./chartjs');
44
45
  const { GoogleCharts } = require('./google_charts');
45
46
  const { GoogleRecaptcha } = require('./google_recaptcha');
46
47
  const { GoogleMap } = require('./google_maps');
@@ -49,8 +50,8 @@ const { WebSocket } = require('./web_socket');
49
50
 
50
51
  module.exports = {
51
52
  Array, Object, Number, String,
52
- HTTPClient, HTTPRequest, Cookie, UrlAndQueryString, IBAN, BankCard, AudioMedia, VideoMedia, UserMedia, PersonName, Email, TelephoneNumber, DateTime, TimestampUnix, SqlDate, SqlTime, SqlDateTime, Duration, File, CSV, Img, FormHelper, Country, PostalAddress, GeographicCoordinates, HexColor, RgbColor, SocialNetwork, NumberFormatter
53
+ HTTPClient, HTTPRequest, Cookie, UrlAndQueryString, IBAN, BankCard, AudioMedia, VideoMedia, UserMedia, PersonName, Email, TelephoneNumber, DateTime, DatePeriod, TimestampUnix, SqlDate, SqlTime, SqlDateTime, Duration, File, CSV, Img, FormHelper, Country, PostalAddress, GeographicCoordinates, HexColor, RgbColor, SocialNetwork, NumberFormatter,
53
54
  Browser, DataTable, Pagination, Navigation, DetailsSubArray, SelectAll, MultipleActionInTable, MultipleActionInDivList, EditValue, FormDate, InputPeriod, ShoppingCart, FlashMessage, CountDown, ImportFromCsv, JwtToken, JwtSession, ApiTokenSession, ListBox, WebRTC, WebSocket, EventBus,
54
55
  sleep, refresh, chr, ord, trim, empty,
55
- GoogleCharts, GoogleRecaptcha, GoogleMap, OpenStreetMap
56
+ Chartjs, GoogleCharts, GoogleRecaptcha, GoogleMap, OpenStreetMap
56
57
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osimatic/helpers-js",
3
- "version": "1.2.7",
3
+ "version": "1.3.0",
4
4
  "main": "main.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"