px-jspreadsheet-ce 0.0.1

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +292 -0
  3. package/dist/index.d.ts +2382 -0
  4. package/dist/index.js +11286 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/jspreadsheet.css +723 -0
  7. package/dist/jspreadsheet.themes.css +104 -0
  8. package/package.json +57 -0
  9. package/src/index.js +95 -0
  10. package/src/test.js +50 -0
  11. package/src/utils/cells.js +36 -0
  12. package/src/utils/columns.js +742 -0
  13. package/src/utils/comments.js +87 -0
  14. package/src/utils/config.js +46 -0
  15. package/src/utils/copyPaste.js +438 -0
  16. package/src/utils/data.js +419 -0
  17. package/src/utils/dispatch.js +115 -0
  18. package/src/utils/download.js +38 -0
  19. package/src/utils/editor.js +430 -0
  20. package/src/utils/events.js +1639 -0
  21. package/src/utils/factory.js +216 -0
  22. package/src/utils/filter.js +128 -0
  23. package/src/utils/footer.js +51 -0
  24. package/src/utils/freeze.js +19 -0
  25. package/src/utils/headers.js +74 -0
  26. package/src/utils/helpers.js +409 -0
  27. package/src/utils/history.js +336 -0
  28. package/src/utils/internal.js +1299 -0
  29. package/src/utils/internalHelpers.js +96 -0
  30. package/src/utils/keys.js +406 -0
  31. package/src/utils/lazyLoading.js +143 -0
  32. package/src/utils/libraryBase.js +5 -0
  33. package/src/utils/merges.js +275 -0
  34. package/src/utils/meta.js +81 -0
  35. package/src/utils/orderBy.js +185 -0
  36. package/src/utils/pagination.js +181 -0
  37. package/src/utils/rows.js +624 -0
  38. package/src/utils/search.js +83 -0
  39. package/src/utils/selection.js +744 -0
  40. package/src/utils/style.js +147 -0
  41. package/src/utils/toolbar.js +566 -0
  42. package/src/utils/version.js +9 -0
  43. package/src/utils/worksheets.js +731 -0
  44. package/src/webcomponent.js +59 -0
@@ -0,0 +1,1299 @@
1
+ import jSuites from 'jsuites';
2
+ import formula from '@jspreadsheet/formula';
3
+
4
+ import dispatch from './dispatch.js';
5
+ import { refreshSelection, updateCornerPosition } from './selection.js';
6
+ import { getCellNameFromCoords, getColumnName } from './helpers.js';
7
+ import { updateMeta } from './meta.js';
8
+ import { getFreezeWidth } from './freeze.js';
9
+ import { updatePagination } from './pagination.js';
10
+ import { setFooter } from './footer.js';
11
+ import { getColumnNameFromId, getIdFromColumnName, getFreezeColumnLeft, getFreezeRowTop } from './internalHelpers.js';
12
+
13
+ export const updateTable = function () {
14
+ const obj = this;
15
+
16
+ // Check for spare
17
+ if (obj.options.minSpareRows > 0) {
18
+ let numBlankRows = 0;
19
+ for (let j = obj.rows.length - 1; j >= 0; j--) {
20
+ let test = false;
21
+ for (let i = 0; i < obj.headers.length; i++) {
22
+ if (obj.options.data[j][i]) {
23
+ test = true;
24
+ }
25
+ }
26
+ if (test) {
27
+ break;
28
+ } else {
29
+ numBlankRows++;
30
+ }
31
+ }
32
+
33
+ if (obj.options.minSpareRows - numBlankRows > 0) {
34
+ obj.insertRow(obj.options.minSpareRows - numBlankRows);
35
+ }
36
+ }
37
+
38
+ if (obj.options.minSpareCols > 0) {
39
+ let numBlankCols = 0;
40
+ for (let i = obj.headers.length - 1; i >= 0; i--) {
41
+ let test = false;
42
+ for (let j = 0; j < obj.rows.length; j++) {
43
+ if (obj.options.data[j][i]) {
44
+ test = true;
45
+ }
46
+ }
47
+ if (test) {
48
+ break;
49
+ } else {
50
+ numBlankCols++;
51
+ }
52
+ }
53
+
54
+ if (obj.options.minSpareCols - numBlankCols > 0) {
55
+ obj.insertColumn(obj.options.minSpareCols - numBlankCols);
56
+ }
57
+ }
58
+
59
+ // Update footers
60
+ if (obj.options.footers) {
61
+ setFooter.call(obj);
62
+ }
63
+
64
+ if (obj.options.columns.length < obj.options.minDimensions[0]) {
65
+ obj.options.minDimensions[0] = obj.options.columns.length;
66
+ }
67
+
68
+ // Update corner position
69
+ setTimeout(function () {
70
+ updateCornerPosition.call(obj);
71
+ }, 0);
72
+ };
73
+
74
+ /**
75
+ * Trying to extract a number from a string
76
+ */
77
+ const parseNumber = function (value, i, j) {
78
+ const obj = this;
79
+
80
+ let config = obj.options.columns && obj.options.columns[i];
81
+
82
+ const cellName = getCellNameFromCoords(i, j);
83
+ const cells = obj.options.cells;
84
+ if (typeof cells === 'object' && cells[cellName]) {
85
+ config = cells[cellName];
86
+ }
87
+
88
+ // Decimal point
89
+ const decimal = config && config.decimal ? config : '.';
90
+
91
+ // Parse both parts of the number
92
+ let number = '' + value;
93
+ number = number.split(decimal);
94
+ number[0] = number[0].match(/[+-]?[0-9]/g);
95
+ if (number[0]) {
96
+ number[0] = number[0].join('');
97
+ }
98
+ if (number[1]) {
99
+ number[1] = number[1].match(/[0-9]*/g).join('');
100
+ }
101
+
102
+ // Is a valid number
103
+ if (number[0] && Number.isInteger(Number(number[0]))) {
104
+ if (!number[1]) {
105
+ value = Number(number[0] + '.00');
106
+ } else {
107
+ value = Number(number[0] + '.' + number[1]);
108
+ }
109
+ } else {
110
+ value = null;
111
+ }
112
+
113
+ return value;
114
+ };
115
+
116
+ /**
117
+ * Parse formulas
118
+ */
119
+ export const executeFormula = function (expression, x, y) {
120
+ const obj = this;
121
+
122
+ const formulaResults = [];
123
+ const formulaLoopProtection = [];
124
+
125
+ // Execute formula with loop protection
126
+ const execute = function (expression, x, y) {
127
+ // Parent column identification
128
+ const parentId = getColumnNameFromId([x, y]);
129
+
130
+ // Code protection
131
+ if (formulaLoopProtection[parentId]) {
132
+ console.error('Reference loop detected');
133
+ return '#ERROR';
134
+ }
135
+
136
+ formulaLoopProtection[parentId] = true;
137
+
138
+ // Convert range tokens
139
+ const tokensUpdate = function (tokens) {
140
+ for (let index = 0; index < tokens.length; index++) {
141
+ const f = [];
142
+ const token = tokens[index].split(':');
143
+ const e1 = getIdFromColumnName(token[0], true);
144
+ const e2 = getIdFromColumnName(token[1], true);
145
+
146
+ let x1, x2;
147
+
148
+ if (e1[0] <= e2[0]) {
149
+ x1 = e1[0];
150
+ x2 = e2[0];
151
+ } else {
152
+ x1 = e2[0];
153
+ x2 = e1[0];
154
+ }
155
+
156
+ let y1, y2;
157
+
158
+ if (e1[1] <= e2[1]) {
159
+ y1 = e1[1];
160
+ y2 = e2[1];
161
+ } else {
162
+ y1 = e2[1];
163
+ y2 = e1[1];
164
+ }
165
+
166
+ for (let j = y1; j <= y2; j++) {
167
+ for (let i = x1; i <= x2; i++) {
168
+ f.push(getColumnNameFromId([i, j]));
169
+ }
170
+ }
171
+
172
+ expression = expression.replace(tokens[index], f.join(','));
173
+ }
174
+ };
175
+
176
+ // Range with $ remove $
177
+ expression = expression.replace(/\$?([A-Z]+)\$?([0-9]+)/g, '$1$2');
178
+
179
+ let tokens = expression.match(/([A-Z]+[0-9]+):([A-Z]+[0-9]+)/g);
180
+ if (tokens && tokens.length) {
181
+ tokensUpdate(tokens);
182
+ }
183
+
184
+ // Get tokens
185
+ tokens = expression.match(/([A-Z]+[0-9]+)/g);
186
+
187
+ // Direct self-reference protection
188
+ if (tokens && tokens.indexOf(parentId) > -1) {
189
+ console.error('Self Reference detected');
190
+ return '#ERROR';
191
+ } else {
192
+ // Expressions to be used in the parsing
193
+ const formulaExpressions = {};
194
+
195
+ if (tokens) {
196
+ for (let i = 0; i < tokens.length; i++) {
197
+ // Keep chain
198
+ if (!obj.formula[tokens[i]]) {
199
+ obj.formula[tokens[i]] = [];
200
+ }
201
+ // Is already in the register
202
+ if (obj.formula[tokens[i]].indexOf(parentId) < 0) {
203
+ obj.formula[tokens[i]].push(parentId);
204
+ }
205
+
206
+ // Do not calculate again
207
+ if (eval('typeof(' + tokens[i] + ') == "undefined"')) {
208
+ // Coords
209
+ const position = getIdFromColumnName(tokens[i], 1);
210
+ // Get value
211
+ let value;
212
+
213
+ if (typeof obj.options.data[position[1]] != 'undefined' && typeof obj.options.data[position[1]][position[0]] != 'undefined') {
214
+ value = obj.options.data[position[1]][position[0]];
215
+ } else {
216
+ value = '';
217
+ }
218
+ // Get column data
219
+ if (('' + value).substr(0, 1) == '=') {
220
+ if (typeof formulaResults[tokens[i]] !== 'undefined') {
221
+ value = formulaResults[tokens[i]];
222
+ } else {
223
+ value = execute(value, position[0], position[1]);
224
+ formulaResults[tokens[i]] = value;
225
+ }
226
+ }
227
+ // Type!
228
+ if (('' + value).trim() == '') {
229
+ // Null
230
+ formulaExpressions[tokens[i]] = null;
231
+ } else {
232
+ if (value == Number(value) && obj.parent.config.autoCasting != false) {
233
+ // Number
234
+ formulaExpressions[tokens[i]] = Number(value);
235
+ } else {
236
+ // Trying any formatted number
237
+ const number = parseNumber.call(obj, value, position[0], position[1]);
238
+ if (obj.parent.config.autoCasting != false && number) {
239
+ formulaExpressions[tokens[i]] = number;
240
+ } else {
241
+ formulaExpressions[tokens[i]] = '"' + value + '"';
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ const ret = dispatch.call(obj, 'onbeforeformula', obj, expression, x, y);
250
+ if (ret === false) {
251
+ return expression;
252
+ } else if (ret) {
253
+ expression = ret;
254
+ }
255
+
256
+ // Convert formula to javascript
257
+ let res;
258
+
259
+ try {
260
+ res = formula(expression.substr(1), formulaExpressions, x, y, obj);
261
+
262
+ if (typeof res === 'function') {
263
+ res = '#ERROR';
264
+ }
265
+ } catch (e) {
266
+ res = '#ERROR';
267
+
268
+ if (obj.parent.config.debugFormulas === true) {
269
+ console.log(expression.substr(1), formulaExpressions, e);
270
+ }
271
+ }
272
+
273
+ return res;
274
+ }
275
+ };
276
+
277
+ return execute(expression, x, y);
278
+ };
279
+
280
+ export const parseValue = function (i, j, value, cell) {
281
+ const obj = this;
282
+
283
+ if (('' + value).substr(0, 1) == '=' && obj.parent.config.parseFormulas != false) {
284
+ value = executeFormula.call(obj, value, i, j);
285
+ }
286
+
287
+ let config = obj.options.columns && obj.options.columns[i];
288
+
289
+ const cellName = getCellNameFromCoords(i, j);
290
+ const cells = obj.options.cells;
291
+ if (typeof cells === 'object' && cells[cellName]) {
292
+ config = cells[cellName];
293
+ }
294
+ if (config && !isFormula(value)) {
295
+ // Mask options
296
+ let opt = null;
297
+ if ((opt = getMask(config))) {
298
+ if (value && value == Number(value)) {
299
+ value = Number(value);
300
+ }
301
+ // Process the decimals to match the mask
302
+ let masked = jSuites.mask.render(value, opt, true);
303
+ // Negative indication
304
+ if (cell) {
305
+ if (opt.mask) {
306
+ const t = opt.mask.split(';');
307
+ if (t[1]) {
308
+ const t1 = t[1].match(new RegExp('\\[Red\\]', 'gi'));
309
+ if (t1) {
310
+ if (value < 0) {
311
+ cell.classList.add('red');
312
+ } else {
313
+ cell.classList.remove('red');
314
+ }
315
+ }
316
+ const t2 = t[1].match(new RegExp('\\(', 'gi'));
317
+ if (t2) {
318
+ if (value < 0) {
319
+ masked = '(' + masked + ')';
320
+ }
321
+ }
322
+ }
323
+ }
324
+ }
325
+
326
+ if (masked) {
327
+ value = masked;
328
+ }
329
+ }
330
+ }
331
+
332
+ return value;
333
+ };
334
+
335
+ /**
336
+ * Get dropdown value from key
337
+ */
338
+ const getDropDownValue = function (source, key) {
339
+ const obj = this;
340
+
341
+ const value = [];
342
+
343
+ if (source) {
344
+ // Create array from source
345
+ const combo = [];
346
+
347
+ for (let i = 0; i < source.length; i++) {
348
+ if (typeof source[i] == 'object') {
349
+ combo[source[i].id] = source[i].name;
350
+ } else {
351
+ combo[source[i]] = source[i];
352
+ }
353
+ }
354
+
355
+ // Guarantee single multiple compatibility
356
+ const keys = Array.isArray(key) ? key : ('' + key).split(';');
357
+
358
+ for (let i = 0; i < keys.length; i++) {
359
+ if (typeof keys[i] === 'object') {
360
+ value.push(combo[keys[i].id]);
361
+ } else {
362
+ if (combo[keys[i]]) {
363
+ value.push(combo[keys[i]]);
364
+ }
365
+ }
366
+ }
367
+ } else {
368
+ console.error('Invalid column');
369
+ }
370
+
371
+ return value.length > 0 ? value.join('; ') : '';
372
+ };
373
+
374
+ const validDate = function (date) {
375
+ date = '' + date;
376
+ if (date.substr(4, 1) == '-' && date.substr(7, 1) == '-') {
377
+ return true;
378
+ } else {
379
+ date = date.split('-');
380
+ if (date[0].length == 4 && date[0] == Number(date[0]) && date[1].length == 2 && date[1] == Number(date[1])) {
381
+ return true;
382
+ }
383
+ }
384
+ return false;
385
+ };
386
+
387
+ /**
388
+ * Strip tags
389
+ */
390
+ const stripScript = function (a) {
391
+ const b = new Option();
392
+ b.innerHTML = a;
393
+ let c = null;
394
+ for (a = b.getElementsByTagName('script'); (c = a[0]); ) c.parentNode.removeChild(c);
395
+ return b.innerHTML;
396
+ };
397
+ // i:col j: row
398
+ export const createCell = function (i, j, value) {
399
+ const obj = this;
400
+
401
+ // Create cell and properties
402
+ let td = document.createElement('td');
403
+ td.setAttribute('data-x', i);
404
+ td.setAttribute('data-y', j);
405
+
406
+ if (obj.headers[i].style.display === 'none') {
407
+ td.style.display = 'none';
408
+ }
409
+ // Security
410
+ if (('' + value).substr(0, 1) == '=' && obj.options.secureFormulas == true) {
411
+ const val = secureFormula(value);
412
+ if (val != value) {
413
+ // Update the data container
414
+ value = val;
415
+ }
416
+ }
417
+
418
+ let config = obj.options.columns && obj.options.columns[i];
419
+
420
+ const cellName = getCellNameFromCoords(i, j);
421
+ const cells = obj.options.cells;
422
+ if (typeof cells === 'object' && cells[cellName]) {
423
+ config = cells[cellName];
424
+ }
425
+ // 先默认处理,然后渲染定制editor
426
+ // Hidden column
427
+ if (config && config.type == 'hidden') {
428
+ td.style.display = 'none';
429
+ td.textContent = value;
430
+ } else if (config && (config.type == 'checkbox' || config.type == 'radio')) {
431
+ // Create input
432
+ const element = document.createElement('input');
433
+ element.type = config.type;
434
+ element.name = 'c' + i;
435
+ element.checked = value == 1 || value == true || value == 'true' ? true : false;
436
+ element.onclick = function () {
437
+ obj.setValue(td, this.checked);
438
+ };
439
+
440
+ if (config.readOnly == true || obj.options.editable == false) {
441
+ element.setAttribute('disabled', 'disabled');
442
+ }
443
+
444
+ // Append to the table
445
+ td.appendChild(element);
446
+ // Make sure the values are correct
447
+ obj.options.data[j][i] = element.checked;
448
+ } else if (config && config.type == 'calendar') {
449
+ // Try formatted date
450
+ let formatted = null;
451
+ if (!validDate(value)) {
452
+ const tmp = jSuites.calendar.extractDateFromString(value, (config.options && config.options.format) || 'YYYY-MM-DD');
453
+ if (tmp) {
454
+ formatted = tmp;
455
+ }
456
+ }
457
+ // Create calendar cell
458
+ td.textContent = jSuites.calendar.getDateString(formatted ? formatted : value, config.options && config.options.format);
459
+ } else if (config && config.type == 'dropdown') {
460
+ // Create dropdown cell
461
+ td.classList.add('jss_dropdown');
462
+ td.textContent = getDropDownValue.call(obj, config.source, value);
463
+ } else if (config && config.type == 'color') {
464
+ if (config.render == 'square') {
465
+ const color = document.createElement('div');
466
+ color.className = 'color';
467
+ color.style.backgroundColor = value;
468
+ td.appendChild(color);
469
+ } else {
470
+ td.style.color = value;
471
+ td.textContent = value;
472
+ }
473
+ } else if (config && config.type == 'image') {
474
+ if (value && value.substr(0, 10) == 'data:image') {
475
+ const img = document.createElement('img');
476
+ img.src = value;
477
+ td.appendChild(img);
478
+ }
479
+ } else {
480
+ if (config && config.type == 'html') {
481
+ td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));
482
+ } else {
483
+ if (obj.parent.config.parseHTML === true) {
484
+ td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));
485
+ } else {
486
+ td.textContent = parseValue.call(this, i, j, value, td);
487
+ }
488
+ }
489
+ }
490
+
491
+ // Custom column
492
+ if (config && typeof config.type === 'object') {
493
+ if (obj.parent.config.parseHTML === true) {
494
+ td.innerHTML = value;
495
+ }
496
+ if (typeof config.type.createCell == 'function') {
497
+ config.type.createCell(td, value, parseInt(i), parseInt(j), obj, config);
498
+ }
499
+ }
500
+
501
+ // Readonly
502
+ if (config && config.readOnly == true) {
503
+ td.classList.add('readonly');
504
+ }
505
+
506
+ if (obj.options.freezeRows > j) {
507
+ td.classList.add('jss_frozen_row');
508
+ td.style.top = getFreezeRowTop(j, obj.options.rows);
509
+ }
510
+
511
+ if (obj.options.freezeColumns > i) {
512
+ td.classList.add('jss_frozen');
513
+ td.style.left = getFreezeColumnLeft(i, obj.options.columns);
514
+ }
515
+
516
+ // Text align
517
+ const colAlign = (config && config.align) || obj.options.defaultColAlign || 'center';
518
+ td.style.textAlign = colAlign;
519
+
520
+ // Wrap option
521
+ if ((!config || config.wordWrap != false) && (obj.options.wordWrap == true || (config && config.wordWrap == true) || td.innerHTML.length > 200)) {
522
+ td.style.whiteSpace = 'pre-wrap';
523
+ }
524
+
525
+ // Overflow
526
+ if (i > 0) {
527
+ if (obj.options.textOverflow == true) {
528
+ if (value || td.innerHTML) {
529
+ obj.records[j][i - 1].element.style.overflow = 'hidden';
530
+ } else {
531
+ if (i == obj.options.columns.length - 1) {
532
+ td.style.overflow = 'hidden';
533
+ }
534
+ }
535
+ }
536
+ }
537
+
538
+ dispatch.call(obj, 'oncreatecell', obj, td, i, j, value);
539
+
540
+ return td;
541
+ };
542
+
543
+ /**
544
+ * Update cell content
545
+ *
546
+ * @param object cell
547
+ * @return void
548
+ */
549
+ export const updateCell = function (x, y, value, force) {
550
+ const obj = this;
551
+
552
+ let record;
553
+
554
+ // Changing value depending on the column type
555
+ if (obj.records[y][x].element.classList.contains('readonly') == true && !force) {
556
+ // Do nothing
557
+ record = {
558
+ x: x,
559
+ y: y,
560
+ col: x,
561
+ row: y,
562
+ };
563
+ } else {
564
+ // Security
565
+ if (('' + value).substr(0, 1) == '=' && obj.options.secureFormulas == true) {
566
+ const val = secureFormula(value);
567
+ if (val != value) {
568
+ // Update the data container
569
+ value = val;
570
+ }
571
+ }
572
+
573
+ // On change
574
+ const val = dispatch.call(obj, 'onbeforechange', obj, obj.records[y][x].element, x, y, value);
575
+
576
+ // If you return something this will overwrite the value
577
+ if (val != undefined) {
578
+ value = val;
579
+ }
580
+
581
+ let config = obj.options.columns && obj.options.columns[x];
582
+ const cellName = getCellNameFromCoords(x, y);
583
+ const cells = obj.options.cells;
584
+ if (typeof cells === 'object' && cells[cellName]) {
585
+ config = cells[cellName];
586
+ }
587
+
588
+ // custom editor
589
+ if (config && typeof config.type === 'object' && typeof config.type.updateCell === 'function') {
590
+ const result = config.type.updateCell(obj.records[y][x].element, value, parseInt(x), parseInt(y), obj, config);
591
+
592
+ if (result !== undefined) {
593
+ value = result;
594
+ }
595
+ }
596
+
597
+ // History format
598
+ record = {
599
+ x: x,
600
+ y: y,
601
+ col: x,
602
+ row: y,
603
+ value: value,
604
+ oldValue: obj.options.data[y][x],
605
+ };
606
+
607
+ let editor = config && typeof config.type === 'object' ? config.type : null;
608
+ if (editor) {
609
+ // Update data and cell
610
+ obj.options.data[y][x] = value;
611
+ if (typeof editor.setValue === 'function') {
612
+ editor.setValue(obj.records[y][x].element, value);
613
+ }
614
+ } else {
615
+ // Native functions
616
+ if (config && (config.type == 'checkbox' || config.type == 'radio')) {
617
+ // Unchecked all options
618
+ if (config.type == 'radio') {
619
+ for (let j = 0; j < obj.options.data.length; j++) {
620
+ obj.options.data[j][x] = false;
621
+ }
622
+ }
623
+
624
+ // Update data and cell
625
+ obj.records[y][x].element.children[0].checked = value == 1 || value == true || value == 'true' || value == 'TRUE' ? true : false;
626
+ obj.options.data[y][x] = obj.records[y][x].element.children[0].checked;
627
+ } else if (config && config.type == 'dropdown') {
628
+ // Update data and cell
629
+ obj.options.data[y][x] = value;
630
+ obj.records[y][x].element.textContent = getDropDownValue.call(obj, config.source, value);
631
+ } else if (config && config.type == 'calendar') {
632
+ // Try formatted date
633
+ let formatted = null;
634
+ if (!validDate(value)) {
635
+ const tmp = jSuites.calendar.extractDateFromString(value, (config.options && config.options.format) || 'YYYY-MM-DD');
636
+ if (tmp) {
637
+ formatted = tmp;
638
+ }
639
+ }
640
+ // Update data and cell
641
+ obj.options.data[y][x] = value;
642
+ obj.records[y][x].element.textContent = jSuites.calendar.getDateString(formatted ? formatted : value, config.options && config.options.format);
643
+ } else if (config && config.type == 'color') {
644
+ // Update color
645
+ obj.options.data[y][x] = value;
646
+ // Render
647
+ if (config.render == 'square') {
648
+ const color = document.createElement('div');
649
+ color.className = 'color';
650
+ color.style.backgroundColor = value;
651
+ obj.records[y][x].element.textContent = '';
652
+ obj.records[y][x].element.appendChild(color);
653
+ } else {
654
+ obj.records[y][x].element.style.color = value;
655
+ obj.records[y][x].element.textContent = value;
656
+ }
657
+ } else if (config && config.type == 'image') {
658
+ value = '' + value;
659
+ obj.options.data[y][x] = value;
660
+ obj.records[y][x].element.innerHTML = '';
661
+ if (value && value.substr(0, 10) == 'data:image') {
662
+ const img = document.createElement('img');
663
+ img.src = value;
664
+ obj.records[y][x].element.appendChild(img);
665
+ }
666
+ } else {
667
+ // Update data and cell
668
+ obj.options.data[y][x] = value;
669
+ // Label
670
+ if (config && config.type == 'html') {
671
+ obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value));
672
+ } else {
673
+ if (obj.parent.config.parseHTML === true) {
674
+ obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value, obj.records[y][x].element));
675
+ } else {
676
+ obj.records[y][x].element.textContent = parseValue.call(obj, x, y, value, obj.records[y][x].element);
677
+ }
678
+ }
679
+ // Handle big text inside a cell
680
+ if (
681
+ (!config || config.wordWrap != false) &&
682
+ (obj.options.wordWrap == true || (config && config.wordWrap == true) || obj.records[y][x].element.innerHTML.length > 200)
683
+ ) {
684
+ obj.records[y][x].element.style.whiteSpace = 'pre-wrap';
685
+ } else {
686
+ obj.records[y][x].element.style.whiteSpace = '';
687
+ }
688
+ }
689
+ }
690
+
691
+ // Overflow
692
+ if (x > 0) {
693
+ if (value) {
694
+ obj.records[y][x - 1].element.style.overflow = 'hidden';
695
+ } else {
696
+ obj.records[y][x - 1].element.style.overflow = '';
697
+ }
698
+ }
699
+
700
+ if (config && typeof config.render === 'function') {
701
+ config.render(obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null, value, parseInt(x), parseInt(y), obj, config);
702
+ }
703
+
704
+ // On change
705
+ dispatch.call(obj, 'onchange', obj, obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null, x, y, value, record.oldValue);
706
+ }
707
+
708
+ return record;
709
+ };
710
+
711
+ /**
712
+ * The value is a formula
713
+ */
714
+ export const isFormula = function (value) {
715
+ const v = ('' + value)[0];
716
+ return v == '=' || v == '#' ? true : false;
717
+ };
718
+
719
+ /**
720
+ * Get the mask in the jSuites.mask format
721
+ */
722
+ export const getMask = function (o) {
723
+ if (o.format || o.mask || o.locale) {
724
+ const opt = {};
725
+ if (o.mask) {
726
+ opt.mask = o.mask;
727
+ } else if (o.format) {
728
+ opt.mask = o.format;
729
+ } else {
730
+ opt.locale = o.locale;
731
+ opt.options = o.options;
732
+ }
733
+
734
+ if (o.decimal) {
735
+ if (!opt.options) {
736
+ opt.options = {};
737
+ }
738
+ opt.options = { decimal: o.decimal };
739
+ }
740
+ return opt;
741
+ }
742
+
743
+ return null;
744
+ };
745
+
746
+ /**
747
+ * Secure formula
748
+ */
749
+ const secureFormula = function (oldValue) {
750
+ let newValue = '';
751
+ let inside = 0;
752
+
753
+ for (let i = 0; i < oldValue.length; i++) {
754
+ if (oldValue[i] == '"') {
755
+ if (inside == 0) {
756
+ inside = 1;
757
+ } else {
758
+ inside = 0;
759
+ }
760
+ }
761
+
762
+ if (inside == 1) {
763
+ newValue += oldValue[i];
764
+ } else {
765
+ newValue += oldValue[i].toUpperCase();
766
+ }
767
+ }
768
+
769
+ return newValue;
770
+ };
771
+
772
+ /**
773
+ * Update all related cells in the chain
774
+ */
775
+ let chainLoopProtection = [];
776
+
777
+ export const updateFormulaChain = function (x, y, records) {
778
+ const obj = this;
779
+
780
+ const cellId = getColumnNameFromId([x, y]);
781
+ if (obj.formula[cellId] && obj.formula[cellId].length > 0) {
782
+ if (chainLoopProtection[cellId]) {
783
+ obj.records[y][x].element.innerHTML = '#ERROR';
784
+ obj.formula[cellId] = '';
785
+ } else {
786
+ // Protection
787
+ chainLoopProtection[cellId] = true;
788
+
789
+ for (let i = 0; i < obj.formula[cellId].length; i++) {
790
+ const cell = getIdFromColumnName(obj.formula[cellId][i], true);
791
+ // Update cell
792
+ const value = '' + obj.options.data[cell[1]][cell[0]];
793
+ if (value.substr(0, 1) == '=') {
794
+ records.push(updateCell.call(obj, cell[0], cell[1], value, true));
795
+ } else {
796
+ // No longer a formula, remove from the chain
797
+ Object.keys(obj.formula)[i] = null;
798
+ }
799
+ updateFormulaChain.call(obj, cell[0], cell[1], records);
800
+ }
801
+ }
802
+ }
803
+
804
+ chainLoopProtection = [];
805
+ };
806
+
807
+ /**
808
+ * Update formula
809
+ */
810
+ export const updateFormula = function (formula, referencesToUpdate) {
811
+ const testLetter = /[A-Z]/;
812
+ const testNumber = /[0-9]/;
813
+
814
+ let newFormula = '';
815
+ let letter = null;
816
+ let number = null;
817
+ let token = '';
818
+
819
+ for (let index = 0; index < formula.length; index++) {
820
+ if (testLetter.exec(formula[index])) {
821
+ letter = 1;
822
+ number = 0;
823
+ token += formula[index];
824
+ } else if (testNumber.exec(formula[index])) {
825
+ number = letter ? 1 : 0;
826
+ token += formula[index];
827
+ } else {
828
+ if (letter && number) {
829
+ token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
830
+ }
831
+ newFormula += token;
832
+ newFormula += formula[index];
833
+ letter = 0;
834
+ number = 0;
835
+ token = '';
836
+ }
837
+ }
838
+
839
+ if (token) {
840
+ if (letter && number) {
841
+ token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
842
+ }
843
+ newFormula += token;
844
+ }
845
+
846
+ return newFormula;
847
+ };
848
+
849
+ /**
850
+ * Update formulas
851
+ */
852
+ const updateFormulas = function (referencesToUpdate) {
853
+ const obj = this;
854
+
855
+ // Update formulas
856
+ for (let j = 0; j < obj.options.data.length; j++) {
857
+ for (let i = 0; i < obj.options.data[0].length; i++) {
858
+ const value = '' + obj.options.data[j][i];
859
+ // Is formula
860
+ if (value.substr(0, 1) == '=') {
861
+ // Replace tokens
862
+ const newFormula = updateFormula(value, referencesToUpdate);
863
+ if (newFormula != value) {
864
+ obj.options.data[j][i] = newFormula;
865
+ }
866
+ }
867
+ }
868
+ }
869
+
870
+ // Update formula chain
871
+ const formula = [];
872
+ const keys = Object.keys(obj.formula);
873
+ for (let j = 0; j < keys.length; j++) {
874
+ // Current key and values
875
+ let key = keys[j];
876
+ const value = obj.formula[key];
877
+ // Update key
878
+ if (referencesToUpdate[key]) {
879
+ key = referencesToUpdate[key];
880
+ }
881
+ // Update values
882
+ formula[key] = [];
883
+ for (let i = 0; i < value.length; i++) {
884
+ let letter = value[i];
885
+ if (referencesToUpdate[letter]) {
886
+ letter = referencesToUpdate[letter];
887
+ }
888
+ formula[key].push(letter);
889
+ }
890
+ }
891
+ obj.formula = formula;
892
+ };
893
+
894
+ /**
895
+ * Update cell references
896
+ *
897
+ * @return void
898
+ */
899
+ export const updateTableReferences = function () {
900
+ const obj = this;
901
+ if (obj.skipUpdateTableReferences) {
902
+ return;
903
+ }
904
+
905
+ // Update headers
906
+ for (let i = 0; i < obj.headers.length; i++) {
907
+ const x = obj.headers[i].getAttribute('data-x');
908
+
909
+ if (x != i) {
910
+ // Update coords
911
+ obj.headers[i].setAttribute('data-x', i);
912
+ // Title
913
+ if (!obj.headers[i].getAttribute('title')) {
914
+ obj.headers[i].innerHTML = getColumnName(i);
915
+ }
916
+ }
917
+ }
918
+
919
+ // Update all rows
920
+ for (let j = 0; j < obj.rows.length; j++) {
921
+ if (obj.rows[j]) {
922
+ const y = obj.rows[j].element.getAttribute('data-y');
923
+
924
+ if (y != j) {
925
+ // Update coords
926
+ obj.rows[j].element.setAttribute('data-y', j);
927
+ obj.rows[j].element.children[0].setAttribute('data-y', j);
928
+ // Row number
929
+ obj.rows[j].element.children[0].innerHTML = j + 1;
930
+ }
931
+ }
932
+ }
933
+
934
+ // Regular cells affected by this change
935
+ const affectedTokens = [];
936
+ const mergeCellUpdates = [];
937
+
938
+ // Update cell
939
+ const updatePosition = function (x, y, i, j) {
940
+ if (x != i) {
941
+ obj.records[j][i].element.setAttribute('data-x', i);
942
+ }
943
+ if (y != j) {
944
+ obj.records[j][i].element.setAttribute('data-y', j);
945
+ }
946
+
947
+ // Other updates
948
+ if (x != i || y != j) {
949
+ const columnIdFrom = getColumnNameFromId([x, y]);
950
+ const columnIdTo = getColumnNameFromId([i, j]);
951
+ affectedTokens[columnIdFrom] = columnIdTo;
952
+ }
953
+ };
954
+
955
+ for (let j = 0; j < obj.records.length; j++) {
956
+ for (let i = 0; i < obj.records[0].length; i++) {
957
+ if (obj.records[j][i]) {
958
+ // Current values
959
+ const x = obj.records[j][i].element.getAttribute('data-x');
960
+ const y = obj.records[j][i].element.getAttribute('data-y');
961
+
962
+ // Update column
963
+ if (obj.records[j][i].element.getAttribute('data-merged')) {
964
+ const columnIdFrom = getColumnNameFromId([x, y]);
965
+ const columnIdTo = getColumnNameFromId([i, j]);
966
+ if (mergeCellUpdates[columnIdFrom] == null) {
967
+ if (columnIdFrom == columnIdTo) {
968
+ mergeCellUpdates[columnIdFrom] = false;
969
+ } else {
970
+ const totalX = parseInt(i - x);
971
+ const totalY = parseInt(j - y);
972
+ mergeCellUpdates[columnIdFrom] = [columnIdTo, totalX, totalY];
973
+ }
974
+ }
975
+ } else {
976
+ updatePosition(x, y, i, j);
977
+ }
978
+ }
979
+ }
980
+ }
981
+
982
+ // Update merged if applicable
983
+ const keys = Object.keys(mergeCellUpdates);
984
+ if (keys.length) {
985
+ for (let i = 0; i < keys.length; i++) {
986
+ if (mergeCellUpdates[keys[i]]) {
987
+ const info = getIdFromColumnName(keys[i], true);
988
+ let x = info[0];
989
+ let y = info[1];
990
+ updatePosition(x, y, x + mergeCellUpdates[keys[i]][1], y + mergeCellUpdates[keys[i]][2]);
991
+
992
+ const columnIdFrom = keys[i];
993
+ const columnIdTo = mergeCellUpdates[keys[i]][0];
994
+ for (let j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) {
995
+ x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x'));
996
+ y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y'));
997
+ obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]);
998
+ obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]);
999
+ }
1000
+
1001
+ obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom];
1002
+ delete obj.options.mergeCells[columnIdFrom];
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ // Update formulas
1008
+ updateFormulas.call(obj, affectedTokens);
1009
+
1010
+ // Update meta data
1011
+ updateMeta.call(obj, affectedTokens);
1012
+
1013
+ // Refresh selection
1014
+ refreshSelection.call(obj);
1015
+
1016
+ // Update table with custom configuration if applicable
1017
+ updateTable.call(obj);
1018
+ };
1019
+
1020
+ /**
1021
+ * Update scroll position based on the selection
1022
+ */
1023
+ export const updateScroll = function (direction) {
1024
+ const obj = this;
1025
+
1026
+ // Jspreadsheet Container information
1027
+ const contentRect = obj.content.getBoundingClientRect();
1028
+ const x1 = contentRect.left;
1029
+ const y1 = contentRect.top;
1030
+ const w1 = contentRect.width;
1031
+ const h1 = contentRect.height;
1032
+
1033
+ // Direction Left or Up
1034
+ const reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]].element;
1035
+
1036
+ // Reference
1037
+ const referenceRect = reference.getBoundingClientRect();
1038
+ const x2 = referenceRect.left;
1039
+ const y2 = referenceRect.top;
1040
+ const w2 = referenceRect.width;
1041
+ const h2 = referenceRect.height;
1042
+
1043
+ let x, y;
1044
+
1045
+ // Direction
1046
+ if (direction == 0 || direction == 1) {
1047
+ x = x2 - x1 + obj.content.scrollLeft;
1048
+ y = y2 - y1 + obj.content.scrollTop - 2;
1049
+ } else {
1050
+ x = x2 - x1 + obj.content.scrollLeft + w2;
1051
+ y = y2 - y1 + obj.content.scrollTop + h2;
1052
+ }
1053
+
1054
+ // Top position check
1055
+ if (y > obj.content.scrollTop + 30 && y < obj.content.scrollTop + h1) {
1056
+ // In the viewport
1057
+ } else {
1058
+ // Out of viewport
1059
+ if (y < obj.content.scrollTop + 30) {
1060
+ obj.content.scrollTop = y - h2;
1061
+ } else {
1062
+ obj.content.scrollTop = y - (h1 - 2);
1063
+ }
1064
+ }
1065
+
1066
+ // Freeze columns?
1067
+ const freezed = getFreezeWidth.call(obj);
1068
+
1069
+ // Left position check - TODO: change that to the bottom border of the element
1070
+ if (x > obj.content.scrollLeft + freezed && x < obj.content.scrollLeft + w1) {
1071
+ // In the viewport
1072
+ } else {
1073
+ // Out of viewport
1074
+ if (x < obj.content.scrollLeft + 30) {
1075
+ obj.content.scrollLeft = x;
1076
+ if (obj.content.scrollLeft < 50) {
1077
+ obj.content.scrollLeft = 0;
1078
+ }
1079
+ } else if (x < obj.content.scrollLeft + freezed) {
1080
+ obj.content.scrollLeft = x - freezed - 1;
1081
+ } else {
1082
+ obj.content.scrollLeft = x - (w1 - 20);
1083
+ }
1084
+ }
1085
+ };
1086
+
1087
+ export const updateResult = function () {
1088
+ const obj = this;
1089
+
1090
+ let total = 0;
1091
+ let index = 0;
1092
+
1093
+ // Page 1
1094
+ if (obj.options.lazyLoading == true) {
1095
+ total = 100;
1096
+ } else if (obj.options.pagination > 0) {
1097
+ total = obj.options.pagination;
1098
+ } else {
1099
+ if (obj.results) {
1100
+ total = obj.results.length;
1101
+ } else {
1102
+ total = obj.rows.length;
1103
+ }
1104
+ }
1105
+
1106
+ // Reset current nodes
1107
+ while (obj.tbody.firstChild) {
1108
+ obj.tbody.removeChild(obj.tbody.firstChild);
1109
+ }
1110
+
1111
+ // Hide all records from the table
1112
+ for (let j = 0; j < obj.rows.length; j++) {
1113
+ if (!obj.results || obj.results.indexOf(j) > -1) {
1114
+ if (index < total) {
1115
+ obj.tbody.appendChild(obj.rows[j].element);
1116
+ index++;
1117
+ }
1118
+ obj.rows[j].element.style.display = '';
1119
+ } else {
1120
+ obj.rows[j].element.style.display = 'none';
1121
+ }
1122
+ }
1123
+
1124
+ // Update pagination
1125
+ if (obj.options.pagination > 0) {
1126
+ updatePagination.call(obj);
1127
+ }
1128
+
1129
+ updateCornerPosition.call(obj);
1130
+
1131
+ dispatch.call(obj, 'onupdateresult', obj, obj.results);
1132
+
1133
+ return total;
1134
+ };
1135
+
1136
+ /**
1137
+ * Get the cell object
1138
+ *
1139
+ * @param object cell
1140
+ * @return string value
1141
+ */
1142
+ export const getCell = function (x, y) {
1143
+ const obj = this;
1144
+
1145
+ if (typeof x === 'string') {
1146
+ // Convert in case name is excel liked ex. A10, BB92
1147
+ const cell = getIdFromColumnName(x, true);
1148
+
1149
+ x = cell[0];
1150
+ y = cell[1];
1151
+ }
1152
+
1153
+ return obj.records[y][x].element;
1154
+ };
1155
+
1156
+ /**
1157
+ * Get the cell object from coords
1158
+ *
1159
+ * @param object cell
1160
+ * @return string value
1161
+ */
1162
+ export const getCellFromCoords = function (x, y) {
1163
+ const obj = this;
1164
+
1165
+ return obj.records[y][x].element;
1166
+ };
1167
+
1168
+ /**
1169
+ * Get label
1170
+ *
1171
+ * @param object cell
1172
+ * @return string value
1173
+ */
1174
+ export const getLabel = function (x, y) {
1175
+ const obj = this;
1176
+
1177
+ if (typeof x === 'string') {
1178
+ // Convert in case name is excel liked ex. A10, BB92
1179
+ const cell = getIdFromColumnName(x, true);
1180
+
1181
+ x = cell[0];
1182
+ y = cell[1];
1183
+ }
1184
+
1185
+ return obj.records[y][x].element.innerHTML;
1186
+ };
1187
+
1188
+ /**
1189
+ * Activate/Disable fullscreen
1190
+ * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false);
1191
+ * @Param {boolean} activate
1192
+ */
1193
+ export const fullscreen = function (activate) {
1194
+ const spreadsheet = this;
1195
+
1196
+ // If activate not defined, get reverse options.fullscreen
1197
+ if (activate == null) {
1198
+ activate = !spreadsheet.config.fullscreen;
1199
+ }
1200
+
1201
+ // If change
1202
+ if (spreadsheet.config.fullscreen != activate) {
1203
+ spreadsheet.config.fullscreen = activate;
1204
+
1205
+ // Test LazyLoading conflict
1206
+ if (activate == true) {
1207
+ spreadsheet.element.classList.add('fullscreen');
1208
+ } else {
1209
+ spreadsheet.element.classList.remove('fullscreen');
1210
+ }
1211
+ }
1212
+ };
1213
+
1214
+ /**
1215
+ * Show index column
1216
+ */
1217
+ export const showIndex = function () {
1218
+ const obj = this;
1219
+
1220
+ obj.table.classList.remove('jss_hidden_index');
1221
+ };
1222
+
1223
+ /**
1224
+ * Hide index column
1225
+ */
1226
+ export const hideIndex = function () {
1227
+ const obj = this;
1228
+
1229
+ obj.table.classList.add('jss_hidden_index');
1230
+ };
1231
+
1232
+ /**
1233
+ * Create a nested header object
1234
+ */
1235
+ export const createNestedHeader = function (nestedInformation) {
1236
+ const obj = this;
1237
+
1238
+ const tr = document.createElement('tr');
1239
+ tr.classList.add('jss_nested');
1240
+ const td = document.createElement('td');
1241
+ td.classList.add('jss_selectall');
1242
+
1243
+ tr.appendChild(td);
1244
+ // Element
1245
+ nestedInformation.element = tr;
1246
+
1247
+ let headerIndex = 0;
1248
+ for (let i = 0; i < nestedInformation.length; i++) {
1249
+ // Default values
1250
+ if (!nestedInformation[i].colspan) {
1251
+ nestedInformation[i].colspan = 1;
1252
+ }
1253
+ if (!nestedInformation[i].title) {
1254
+ nestedInformation[i].title = '';
1255
+ }
1256
+ if (!nestedInformation[i].id) {
1257
+ nestedInformation[i].id = '';
1258
+ }
1259
+
1260
+ // Number of columns
1261
+ let numberOfColumns = nestedInformation[i].colspan;
1262
+
1263
+ // Classes container
1264
+ const column = [];
1265
+ // Header classes for this cell
1266
+ for (let x = 0; x < numberOfColumns; x++) {
1267
+ if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') {
1268
+ numberOfColumns++;
1269
+ }
1270
+ column.push(headerIndex);
1271
+ headerIndex++;
1272
+ }
1273
+
1274
+ // Created the nested cell
1275
+ const td = document.createElement('td');
1276
+ td.setAttribute('data-column', column.join(','));
1277
+ td.setAttribute('colspan', nestedInformation[i].colspan);
1278
+ td.setAttribute('align', nestedInformation[i].align || 'center');
1279
+ td.setAttribute('id', nestedInformation[i].id);
1280
+ td.textContent = nestedInformation[i].title;
1281
+ tr.appendChild(td);
1282
+ }
1283
+
1284
+ return tr;
1285
+ };
1286
+
1287
+ export const getWorksheetActive = function () {
1288
+ const spreadsheet = this.parent ? this.parent : this;
1289
+
1290
+ return spreadsheet.element.tabs ? spreadsheet.element.tabs.getActive() : 0;
1291
+ };
1292
+
1293
+ export const getWorksheetInstance = function (index) {
1294
+ const spreadsheet = this;
1295
+
1296
+ const worksheetIndex = typeof index !== 'undefined' ? index : getWorksheetActive.call(spreadsheet);
1297
+
1298
+ return spreadsheet.worksheets[worksheetIndex];
1299
+ };