@syncfusion/ej2-inputs 31.1.17 → 31.1.20

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 (106) hide show
  1. package/dist/ej2-inputs.min.js +2 -2
  2. package/dist/ej2-inputs.umd.min.js +2 -2
  3. package/dist/ej2-inputs.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-inputs.es2015.js +4 -3
  5. package/dist/es6/ej2-inputs.es2015.js.map +1 -1
  6. package/dist/es6/ej2-inputs.es5.js +4 -3
  7. package/dist/es6/ej2-inputs.es5.js.map +1 -1
  8. package/dist/global/ej2-inputs.min.js +2 -2
  9. package/dist/global/ej2-inputs.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/package.json +10 -60
  12. package/src/color-picker/color-picker.js +4 -3
  13. package/styles/bootstrap4-lite.css +2 -2
  14. package/styles/bootstrap4.css +2 -2
  15. package/styles/bootstrap5-dark-lite.css +2 -2
  16. package/styles/bootstrap5-dark.css +2 -2
  17. package/styles/bootstrap5-lite.css +2 -2
  18. package/styles/bootstrap5.css +2 -2
  19. package/styles/input/_bootstrap4-definition.scss +1 -1
  20. package/styles/input/_bootstrap5-definition.scss +1 -1
  21. package/styles/input/bootstrap4.css +2 -2
  22. package/styles/input/bootstrap5-dark.css +2 -2
  23. package/styles/input/bootstrap5.css +2 -2
  24. package/dist/ts/color-picker/color-picker-model.d.ts +0 -186
  25. package/dist/ts/color-picker/color-picker.d.ts +0 -398
  26. package/dist/ts/color-picker/color-picker.ts +0 -2209
  27. package/dist/ts/color-picker/index.d.ts +0 -5
  28. package/dist/ts/color-picker/index.ts +0 -5
  29. package/dist/ts/common/index.d.ts +0 -4
  30. package/dist/ts/common/index.ts +0 -4
  31. package/dist/ts/common/signature-base.d.ts +0 -340
  32. package/dist/ts/common/signature-base.ts +0 -1170
  33. package/dist/ts/form-validator/form-validator-model.d.ts +0 -121
  34. package/dist/ts/form-validator/form-validator.d.ts +0 -312
  35. package/dist/ts/form-validator/form-validator.ts +0 -1068
  36. package/dist/ts/form-validator/index.d.ts +0 -5
  37. package/dist/ts/form-validator/index.ts +0 -5
  38. package/dist/ts/index.d.ts +0 -18
  39. package/dist/ts/index.ts +0 -18
  40. package/dist/ts/input/index.d.ts +0 -4
  41. package/dist/ts/input/index.ts +0 -4
  42. package/dist/ts/input/input.d.ts +0 -345
  43. package/dist/ts/input/input.ts +0 -1277
  44. package/dist/ts/maskedtextbox/base/index.d.ts +0 -4
  45. package/dist/ts/maskedtextbox/base/index.ts +0 -4
  46. package/dist/ts/maskedtextbox/base/mask-base.d.ts +0 -134
  47. package/dist/ts/maskedtextbox/base/mask-base.ts +0 -1207
  48. package/dist/ts/maskedtextbox/index.d.ts +0 -5
  49. package/dist/ts/maskedtextbox/index.ts +0 -5
  50. package/dist/ts/maskedtextbox/maskedtextbox/index.d.ts +0 -5
  51. package/dist/ts/maskedtextbox/maskedtextbox/index.ts +0 -5
  52. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox-model.d.ts +0 -167
  53. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.d.ts +0 -307
  54. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.ts +0 -709
  55. package/dist/ts/numerictextbox/index.d.ts +0 -5
  56. package/dist/ts/numerictextbox/index.ts +0 -5
  57. package/dist/ts/numerictextbox/numerictextbox-model.d.ts +0 -228
  58. package/dist/ts/numerictextbox/numerictextbox.d.ts +0 -431
  59. package/dist/ts/numerictextbox/numerictextbox.ts +0 -1672
  60. package/dist/ts/otp-input/index.d.ts +0 -5
  61. package/dist/ts/otp-input/index.ts +0 -5
  62. package/dist/ts/otp-input/otp-input-model.d.ts +0 -165
  63. package/dist/ts/otp-input/otp-input.d.ts +0 -365
  64. package/dist/ts/otp-input/otp-input.ts +0 -924
  65. package/dist/ts/rating/index.d.ts +0 -5
  66. package/dist/ts/rating/index.ts +0 -5
  67. package/dist/ts/rating/rating-model.d.ts +0 -239
  68. package/dist/ts/rating/rating.d.ts +0 -416
  69. package/dist/ts/rating/rating.ts +0 -1101
  70. package/dist/ts/signature/index.d.ts +0 -5
  71. package/dist/ts/signature/index.ts +0 -5
  72. package/dist/ts/signature/signature-model.d.ts +0 -131
  73. package/dist/ts/signature/signature.d.ts +0 -167
  74. package/dist/ts/signature/signature.ts +0 -266
  75. package/dist/ts/slider/index.d.ts +0 -5
  76. package/dist/ts/slider/index.ts +0 -5
  77. package/dist/ts/slider/slider-model.d.ts +0 -401
  78. package/dist/ts/slider/slider.d.ts +0 -721
  79. package/dist/ts/slider/slider.ts +0 -3497
  80. package/dist/ts/smart-textarea/caret-helper.d.ts +0 -17
  81. package/dist/ts/smart-textarea/caret-helper.ts +0 -95
  82. package/dist/ts/smart-textarea/index.d.ts +0 -5
  83. package/dist/ts/smart-textarea/index.ts +0 -5
  84. package/dist/ts/smart-textarea/smart-textarea-model.d.ts +0 -44
  85. package/dist/ts/smart-textarea/smart-textarea.d.ts +0 -80
  86. package/dist/ts/smart-textarea/smart-textarea.ts +0 -518
  87. package/dist/ts/speech-to-text/index.d.ts +0 -5
  88. package/dist/ts/speech-to-text/index.ts +0 -5
  89. package/dist/ts/speech-to-text/speech-to-text-model.d.ts +0 -270
  90. package/dist/ts/speech-to-text/speech-to-text.d.ts +0 -487
  91. package/dist/ts/speech-to-text/speech-to-text.ts +0 -1008
  92. package/dist/ts/textarea/index.d.ts +0 -5
  93. package/dist/ts/textarea/index.ts +0 -5
  94. package/dist/ts/textarea/textarea-model.d.ts +0 -161
  95. package/dist/ts/textarea/textarea.d.ts +0 -237
  96. package/dist/ts/textarea/textarea.ts +0 -812
  97. package/dist/ts/textbox/index.d.ts +0 -5
  98. package/dist/ts/textbox/index.ts +0 -5
  99. package/dist/ts/textbox/textbox-model.d.ts +0 -155
  100. package/dist/ts/textbox/textbox.d.ts +0 -288
  101. package/dist/ts/textbox/textbox.ts +0 -872
  102. package/dist/ts/uploader/index.d.ts +0 -5
  103. package/dist/ts/uploader/index.ts +0 -5
  104. package/dist/ts/uploader/uploader-model.d.ts +0 -555
  105. package/dist/ts/uploader/uploader.d.ts +0 -1216
  106. package/dist/ts/uploader/uploader.ts +0 -4391
@@ -1,4391 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Component, Property, Event, EmitType, EventHandler, L10n, compile, isNullOrUndefined, SanitizeHtmlHelper} from '@syncfusion/ej2-base';
3
- import { NotifyPropertyChanges, INotifyPropertyChanged, detach, append, Animation } from '@syncfusion/ej2-base';
4
- import { addClass, removeClass, KeyboardEvents, KeyboardEventArgs, setValue, getValue, ChildProperty } from '@syncfusion/ej2-base';
5
- import { Collection, Complex, Browser, Ajax, BeforeSendEventArgs, getUniqueID, closest, remove } from '@syncfusion/ej2-base';
6
- import { createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups';
7
- import { UploaderModel, AsyncSettingsModel, ButtonsPropsModel, FilesPropModel } from './uploader-model';
8
- import { select, selectAll } from '@syncfusion/ej2-base';
9
-
10
- const CONTROL_WRAPPER: string = 'e-upload e-control-wrapper';
11
- const INPUT_WRAPPER: string = 'e-file-select';
12
- const DROP_AREA: string = 'e-file-drop';
13
- const DROP_WRAPPER: string = 'e-file-select-wrap';
14
- const LIST_PARENT: string = 'e-upload-files';
15
- const FILE: string = 'e-upload-file-list';
16
- const STATUS: string = 'e-file-status';
17
- const ACTION_BUTTONS: string = 'e-upload-actions';
18
- const UPLOAD_BUTTONS: string = 'e-file-upload-btn e-css e-btn e-flat e-primary';
19
- const CLEAR_BUTTONS: string = 'e-file-clear-btn e-css e-btn e-flat';
20
- const FILE_NAME: string = 'e-file-name';
21
- const FILE_TYPE: string = 'e-file-type';
22
- const FILE_SIZE: string = 'e-file-size';
23
- const REMOVE_ICON: string = 'e-file-remove-btn';
24
- const DELETE_ICON: string = 'e-file-delete-btn';
25
- const SPINNER_PANE: string = 'e-spinner-pane';
26
- const ABORT_ICON: string = 'e-file-abort-btn';
27
- const RETRY_ICON: string = 'e-file-reload-btn';
28
- const DRAG_HOVER: string = 'e-upload-drag-hover';
29
- const PROGRESS_WRAPPER: string = 'e-upload-progress-wrap';
30
- const PROGRESSBAR: string = 'e-upload-progress-bar';
31
- const PROGRESSBAR_TEXT: string = 'e-progress-bar-text';
32
- const UPLOAD_INPROGRESS: string = 'e-upload-progress';
33
- const UPLOAD_SUCCESS: string = 'e-upload-success';
34
- const UPLOAD_FAILED: string = 'e-upload-fails';
35
- const TEXT_CONTAINER: string = 'e-file-container';
36
- const VALIDATION_FAILS: string = 'e-validation-fails';
37
- const RTL: string = 'e-rtl';
38
- const DISABLED : string = 'e-disabled';
39
- const RTL_CONTAINER : string = 'e-rtl-container';
40
- const ICON_FOCUSED : string = 'e-clear-icon-focus';
41
- const PROGRESS_INNER_WRAPPER: string = 'e-progress-inner-wrap';
42
- const PAUSE_UPLOAD: string = 'e-file-pause-btn';
43
- const RESUME_UPLOAD: string = 'e-file-play-btn';
44
- const RESTRICT_RETRY: string = 'e-restrict-retry';
45
- const wrapperAttr: string[] = ['title', 'style', 'class'];
46
- const FORM_UPLOAD: string = 'e-form-upload';
47
- const HIDDEN_INPUT: string = 'e-hidden-file-input';
48
- const INVALID_FILE: string = 'e-file-invalid';
49
- const INFORMATION: string = 'e-file-information';
50
-
51
- export type DropEffect = 'Copy' | 'Move' | 'Link' | 'None'| 'Default';
52
- export class FilesProp extends ChildProperty<FilesProp> {
53
- /**
54
- * Specifies the name of the file
55
- *
56
- * @default ''
57
- */
58
- @Property('')
59
- public name: string;
60
- /**
61
- * Specifies the size of the file
62
- *
63
- * @default null
64
- */
65
- @Property(null)
66
- public size: number;
67
- /**
68
- * Specifies the type of the file
69
- *
70
- * @default ''
71
- */
72
- @Property('')
73
- public type: string;
74
- }
75
-
76
- export class ButtonsProps extends ChildProperty<ButtonsProps> {
77
- /**
78
- * Specifies the text or html content to browse button
79
- *
80
- * @default 'Browse...'
81
- */
82
- @Property('Browse...')
83
- public browse: string | HTMLElement;
84
- /**
85
- * Specifies the text or html content to upload button
86
- *
87
- * @default 'Upload'
88
- */
89
- @Property('Upload')
90
- public upload: string | HTMLElement;
91
- /**
92
- * Specifies the text or html content to clear button
93
- *
94
- * @default 'Clear'
95
- */
96
- @Property('Clear')
97
- public clear: string | HTMLElement;
98
- }
99
-
100
- export class AsyncSettings extends ChildProperty<AsyncSettings> {
101
- /**
102
- * Specifies the URL of save action that will receive the upload files and save in the server.
103
- * The save action type must be POST request and define the argument as same input name used to render the component.
104
- * The upload operations could not perform without this property.
105
- *
106
- * @default ''
107
- */
108
- @Property('')
109
- public saveUrl: string;
110
- /**
111
- * Specifies the URL of remove action that receives the file information and handle the remove operation in server.
112
- * The remove action type must be POST request and define “removeFileNames” attribute to get file information that will be removed.
113
- * This property is optional.
114
- *
115
- * @default ''
116
- */
117
- @Property('')
118
- public removeUrl: string;
119
- /**
120
- * Specifies the chunk size to split the large file into chunks, and upload it to the server in a sequential order.
121
- * If the chunk size property has value, the uploader enables the chunk upload by default.
122
- * It must be specified in bytes value.
123
- *
124
- * > For more information, refer to the [chunk upload](../../uploader/chunk-upload/) section from the documentation.
125
- *
126
- * @default 0
127
- */
128
- @Property(0)
129
- public chunkSize: number;
130
- /**
131
- * Specifies the number of retries that the uploader can perform on the file failed to upload.
132
- * By default, the uploader set 3 as maximum retries. This property must be specified to prevent infinity looping.
133
- *
134
- * @default 3
135
- */
136
- @Property(3)
137
- public retryCount: number;
138
- /**
139
- * Specifies the delay time in milliseconds that the automatic retry happens after the delay.
140
- *
141
- * @default 500
142
- */
143
- @Property(500)
144
- public retryAfterDelay: number;
145
- }
146
-
147
- export interface FileInfo {
148
- /**
149
- * Returns the upload file name.
150
- */
151
- name: string
152
- /**
153
- * Returns the details about upload file.
154
- *
155
- */
156
- rawFile: string | Blob
157
- /**
158
- * Returns the size of file in bytes.
159
- */
160
- size: number
161
- /**
162
- * Returns the status of the file.
163
- */
164
- status: string
165
- /**
166
- * Returns the MIME type of file as a string. Returns empty string if the file’s type is not determined.
167
- */
168
- type: string
169
- /**
170
- * Returns the list of validation errors (if any).
171
- */
172
- validationMessages: ValidationMessages
173
- /**
174
- * Returns the current state of the file such as Failed, Canceled, Selected, Uploaded, or Uploading.
175
- */
176
- statusCode: string
177
- /**
178
- * Returns where the file selected from, to upload.
179
- */
180
- fileSource?: string
181
- /**
182
- * Returns the respective file list item.
183
- */
184
- list ?: HTMLElement
185
- /**
186
- * Returns the input element mapped with file list item.
187
- */
188
- input ?: HTMLInputElement
189
- /**
190
- * Returns the unique upload file name ID.
191
- */
192
- id?: string
193
- }
194
-
195
- export interface MetaData {
196
- chunkIndex: number
197
- blob: Blob | string
198
- file: FileInfo
199
- start: number
200
- end: number
201
- retryCount: number
202
- request: Ajax
203
- }
204
-
205
- export interface ValidationMessages {
206
- /**
207
- * Returns the minimum file size validation message, if selected file size is less than specified minFileSize property.
208
- */
209
- minSize? : string
210
- /**
211
- * Returns the maximum file size validation message, if selected file size is less than specified maxFileSize property.
212
- */
213
- maxSize? : string
214
- }
215
-
216
- export interface SelectedEventArgs {
217
- /**
218
- * Returns the original event arguments.
219
- */
220
- event: MouseEvent | TouchEvent | DragEvent | ClipboardEvent
221
- /**
222
- * Defines whether the current action can be prevented.
223
- */
224
- cancel: boolean
225
- /**
226
- * Returns the list of selected files.
227
- */
228
- filesData: FileInfo[]
229
- /**
230
- * Determines whether the file list generates based on the modified data.
231
- */
232
- isModified: boolean
233
- /**
234
- * Specifies the modified files data to generate the file items. The argument depends on `isModified` argument.
235
- */
236
- modifiedFilesData: FileInfo[]
237
- /**
238
- * Specifies the step value to the progress bar.
239
- */
240
- progressInterval: string
241
- /**
242
- * Specifies whether the file selection has been canceled
243
- */
244
- isCanceled?: boolean
245
- /**
246
- * Set the current request header to the XMLHttpRequest instance.
247
- *
248
- */
249
- currentRequest?: { [key: string]: string }[]
250
- /**
251
- * Defines the additional data in key and value pair format that will be submitted to the upload action.
252
- */
253
- customFormData: { [key: string]: Object }[]
254
- }
255
-
256
- export interface BeforeRemoveEventArgs {
257
- /**
258
- * Defines whether the current action can be prevented.
259
- */
260
- cancel: boolean
261
- /**
262
- * Defines the additional data with key and value pair format that will be submitted to the remove action.
263
- *
264
- */
265
- customFormData: { [key: string]: Object }[]
266
- /**
267
- * Returns the XMLHttpRequest instance that is associated with remove action.
268
- *
269
- */
270
- currentRequest?: { [key: string]: string }[]
271
- }
272
-
273
- export interface RemovingEventArgs {
274
- /**
275
- * Defines whether the current action can be prevented.
276
- */
277
- cancel: boolean
278
- /**
279
- * Defines the additional data with key and value pair format that will be submitted to the remove action.
280
- *
281
- */
282
- customFormData: { [key: string]: Object }[]
283
- /**
284
- * Returns the original event arguments.
285
- */
286
- event: MouseEvent | TouchEvent | KeyboardEventArgs
287
- /**
288
- * Returns the list of files’ details that will be removed.
289
- */
290
- filesData: FileInfo[]
291
- /**
292
- * Returns the XMLHttpRequest instance that is associated with remove action.
293
- *
294
- */
295
- currentRequest?: XMLHttpRequest
296
- /**
297
- * Defines whether the selected raw file send to server remove action.
298
- * Set true to send raw file.
299
- * Set false to send file name only.
300
- */
301
- postRawFile?: boolean
302
- }
303
-
304
- export interface ClearingEventArgs {
305
- /**
306
- * Defines whether the current action can be prevented.
307
- */
308
- cancel: boolean
309
- /**
310
- * Returns the list of files that will be cleared from the FileList.
311
- */
312
- filesData: FileInfo[]
313
- }
314
-
315
- export interface BeforeUploadEventArgs {
316
- /**
317
- * Defines whether the current action can be prevented.
318
- */
319
- cancel: boolean
320
- /**
321
- * Defines the additional data in key and value pair format that will be submitted to the upload action.
322
- *
323
- */
324
- customFormData: { [key: string]: Object }[]
325
- /**
326
- * Returns the XMLHttpRequest instance that is associated with upload action.
327
- *
328
- */
329
- currentRequest?: { [key: string]: string }[]
330
- }
331
-
332
- export interface UploadingEventArgs {
333
- /**
334
- * Returns the list of files that will be uploaded.
335
- */
336
- fileData: FileInfo
337
- /**
338
- * Defines the additional data in key and value pair format that will be submitted to the upload action.
339
- *
340
- * @deprecated
341
- */
342
- customFormData: { [key: string]: Object }[]
343
- /**
344
- * Defines whether the current action can be prevented.
345
- */
346
- cancel: boolean
347
- /**
348
- * Returns the chunk size in bytes if the chunk upload is enabled.
349
- */
350
- chunkSize?: number
351
- /**
352
- * Returns the index of current chunk if the chunk upload is enabled.
353
- */
354
- currentChunkIndex?: number
355
- /**
356
- * Returns the XMLHttpRequest instance that is associated with upload action.
357
- *
358
- * @deprecated
359
- */
360
- currentRequest?: XMLHttpRequest
361
- }
362
-
363
- export interface ProgressEventArgs {
364
- /**
365
- * Returns the original event arguments.
366
- */
367
- e?: object
368
- /**
369
- * Returns the details about upload file.
370
- */
371
- file?: FileInfo
372
- /**
373
- * Returns the upload event operation.
374
- */
375
- operation?: string
376
- }
377
-
378
- export interface UploadChangeEventArgs {
379
- /**
380
- * Returns the list of files that will be cleared from the FileList.
381
- *
382
- */
383
- files?: FileInfo[]
384
- }
385
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
386
- export interface FailureEventArgs extends SuccessEventArgs { }
387
- export interface SuccessEventArgs {
388
- /**
389
- * Returns the original event arguments.
390
- */
391
- e?: object
392
- /**
393
- * Returns the details about upload file.
394
- */
395
- file?: FileInfo
396
- /**
397
- * Returns the upload status.
398
- */
399
- statusText?: string
400
- /**
401
- * Returns the upload event operation.
402
- */
403
- operation: string
404
- /**
405
- * Returns the upload event operation.
406
- */
407
- response?: ResponseEventArgs
408
- /**
409
- * Returns the upload chunk index.
410
- */
411
- chunkIndex?: number
412
- /**
413
- * Returns the upload chunk size.
414
- */
415
- chunkSize?: number
416
- /**
417
- * Returns the total chunk size.
418
- */
419
- totalChunk?: number
420
- /**
421
- * Returns the original event arguments.
422
- */
423
- event?: object
424
- }
425
-
426
- export interface ResponseEventArgs {
427
- headers?: string
428
- readyState?: object
429
- statusCode?: object
430
- statusText?: string
431
- withCredentials?: boolean
432
- }
433
-
434
- export interface CancelEventArgs {
435
- /**
436
- * Defines whether the current action can be prevented.
437
- */
438
- cancel: boolean
439
- /**
440
- * Returns the original event arguments.
441
- */
442
- event: ProgressEventInit
443
- /**
444
- * Returns the file details that will be canceled.
445
- */
446
- fileData: FileInfo
447
- /**
448
- * Defines the additional data in key and value pair format that will be submitted when the upload action is canceled.
449
- *
450
- */
451
- customFormData: { [key: string]: Object }[]
452
- /**
453
- * Defines the additional data in key and value pair format that will be submitted on the header when the upload action is canceled.
454
- *
455
- */
456
- currentRequest?: { [key: string]: string }[]
457
- }
458
-
459
- export interface PauseResumeEventArgs {
460
- /**
461
- * Returns the original event arguments.
462
- */
463
- event: Event
464
- /**
465
- * Returns the file data that is Paused or Resumed.
466
- */
467
- file: FileInfo
468
- /**
469
- * Returns the total number of chunks.
470
- */
471
- chunkCount: number
472
- /**
473
- * Returns the index of chunk that is Paused or Resumed.
474
- */
475
- chunkIndex: number
476
- /**
477
- * Returns the chunk size value in bytes.
478
- */
479
- chunkSize: number
480
- }
481
-
482
- export interface ActionCompleteEventArgs {
483
- /**
484
- * Return the selected file details.
485
- */
486
- fileData: FileInfo[]
487
- }
488
-
489
- export interface RenderingEventArgs {
490
- /**
491
- * Return the current file item element.
492
- */
493
- element: HTMLElement
494
- /**
495
- * Return the current rendering file item data as File object.
496
- */
497
- fileInfo: FileInfo
498
- /**
499
- * Return the index of the file item in the file list.
500
- */
501
- index: number
502
- /**
503
- * Return whether the file is preloaded
504
- */
505
- isPreload: boolean
506
- }
507
-
508
- export interface FileListRenderingEventArgs {
509
- /**
510
- * Return the current file item element.
511
- */
512
- element: HTMLElement
513
- /**
514
- * Return the current rendering file item data as File object.
515
- */
516
- fileInfo: FileInfo
517
- /**
518
- * Return the index of the file item in the file list.
519
- */
520
- index: number
521
- /**
522
- * Return whether the file is preloaded
523
- */
524
- isPreload: boolean
525
- }
526
-
527
- interface InitialAttr {
528
- accept: string
529
- multiple: boolean
530
- disabled: boolean
531
- }
532
-
533
- /**
534
- * The uploader component allows to upload images, documents, and other files from local to server.
535
- * ```html
536
- * <input type='file' name='images[]' id='upload'/>
537
- * ```
538
- * ```typescript
539
- * <script>
540
- * var uploadObj = new Uploader();
541
- * uploadObj.appendTo('#upload');
542
- * </script>
543
- * ```
544
- */
545
-
546
- @NotifyPropertyChanges
547
- export class Uploader extends Component<HTMLInputElement> implements INotifyPropertyChanged {
548
- private initialAttr: InitialAttr = { accept: null, multiple: false, disabled: false };
549
- private uploadWrapper: HTMLElement;
550
- private browseButton: HTMLElement;
551
- private listParent: HTMLElement;
552
- private sortFilesList: FileList;
553
- private actionButtons: HTMLElement;
554
- private uploadButton: HTMLElement;
555
- private clearButton: HTMLElement;
556
- private pauseButton: HTMLElement;
557
- private formElement: HTMLElement;
558
- private dropAreaWrapper: HTMLElement;
559
- private filesEntries: any[];
560
- private uploadedFilesData: FileInfo[] = [];
561
- private base64String: string[] = [];
562
- private currentRequestHeader: { [key: string]: string }[];
563
- private customFormDatas: { [key: string]: object }[];
564
- private dropZoneElement: HTMLElement;
565
- private l10n: L10n;
566
- private preLocaleObj: { [key: string]: Object };
567
- private uploadTemplateFn: Function;
568
- private keyboardModule: KeyboardEvents;
569
- private progressInterval: string;
570
- private progressAnimation: Animation;
571
- private isForm: boolean = false;
572
- private allTypes: boolean = false;
573
- private keyConfigs: { [key: string]: string };
574
- private localeText: { [key: string]: Object };
575
- private pausedData: MetaData[] = [];
576
- private uploadMetaData: MetaData[] = [];
577
- private tabIndex: string = '0';
578
- private btnTabIndex: string = '0';
579
- private disableKeyboardNavigation: boolean = false;
580
- private count: number = -1;
581
- private actionCompleteCount: number = 0;
582
- private flag: boolean = true;
583
- private selectedFiles: FileInfo[] = [];
584
- private browserName: string;
585
- private uploaderOptions: UploaderModel;
586
- private uploaderName: string = 'UploadFiles';
587
- private fileStreams: FileInfo[] = [];
588
- private newFileRef: number = 0;
589
- private isFirstFileOnSelection: boolean = false;
590
- private dragCounter : number = 0;
591
- private isPreloadFiles: boolean;
592
- /**
593
- * Get the file item(li) which are shown in file list.
594
- *
595
- * @private
596
- */
597
- public fileList: HTMLElement[] = [];
598
- /**
599
- * Get the data of files which are shown in file list.
600
- *
601
- * @private
602
- */
603
- public filesData: FileInfo[] = [];
604
- /**
605
- * Configures the save and remove URL to perform the upload operations in the server asynchronously.
606
- *
607
- * @default { saveUrl: '', removeUrl: '' }
608
- */
609
- @Complex<AsyncSettingsModel>({ saveUrl: '', removeUrl: '' }, AsyncSettings)
610
- public asyncSettings: AsyncSettingsModel;
611
-
612
- /**
613
- * By default, the file uploader component is processing the multiple files simultaneously.
614
- * If sequentialUpload property is enabled, the file upload component performs the upload one after the other.
615
- *
616
- * @default false
617
- */
618
- @Property(false)
619
- public sequentialUpload: boolean;
620
-
621
- /**
622
- * You can add the additional html attributes such as disabled, value etc., to the element.
623
- * If you configured both property and equivalent html attribute then the component considers the property value.
624
- *
625
- * {% codeBlock src='uploader/htmlAttributes/index.md' %}{% endcodeBlock %}
626
- *
627
- * @default {}
628
- */
629
- @Property({})
630
- public htmlAttributes: { [key: string]: string };
631
-
632
- /**
633
- * Specifies the CSS class name that can be appended with root element of the uploader.
634
- * One or more custom CSS classes can be added to a uploader.
635
- *
636
- * @default ''
637
- */
638
- @Property('')
639
- public cssClass: string;
640
- /**
641
- * Specifies Boolean value that indicates whether the component is enabled or disabled.
642
- * The uploader component does not allow to interact when this property is disabled.
643
- *
644
- * @default true
645
- */
646
- @Property(true)
647
- public enabled: boolean;
648
-
649
- /**
650
- * Specifies the HTML string that used to customize the content of each file in the list.
651
- *
652
- * > For more information, refer to the [template](../../uploader/template/) section from the documentation.
653
- *
654
- * @default null
655
- * @aspType string
656
- */
657
- @Property(null)
658
- public template: string | Function;
659
-
660
- /**
661
- * Specifies a Boolean value that indicates whether the multiple files can be browsed or
662
- * dropped simultaneously in the uploader component.
663
- *
664
- * @default true
665
- */
666
- @Property(true)
667
- public multiple: boolean;
668
-
669
- /**
670
- * By default, the uploader component initiates automatic upload when the files are added in upload queue.
671
- * If you want to manipulate the files before uploading to server, disable the autoUpload property.
672
- * The buttons “upload” and “clear” will be hided from file list when autoUpload property is true.
673
- *
674
- * @default true
675
- */
676
- @Property(true)
677
- public autoUpload: boolean;
678
-
679
- /**
680
- * Specifies Boolean value that indicates whether to prevent the cross site scripting code in filename or not.
681
- * The uploader component removes the cross-site scripting code or functions from the filename and shows the validation error message to the user when enableHtmlSanitizer is true.
682
- *
683
- * @default true
684
- */
685
- @Property(true)
686
- public enableHtmlSanitizer: boolean;
687
-
688
- /**
689
- * You can customize the default text of “browse, clear, and upload” buttons with plain text or HTML elements.
690
- * The buttons’ text can be customized from localization also. If you configured both locale and buttons property,
691
- * the uploader component considers the buttons property value.
692
- * {% codeBlock src='uploader/buttons/index.md' %}{% endcodeBlock %}
693
- *
694
- * @default { browse : 'Browse...', clear: 'Clear', upload: 'Upload' }
695
- */
696
- @Complex<ButtonsPropsModel>({}, ButtonsProps)
697
- public buttons: ButtonsPropsModel;
698
-
699
- /**
700
- * Specifies the extensions of the file types allowed in the uploader component and pass the extensions
701
- * with comma separators. For example,
702
- * if you want to upload specific image files, pass allowedExtensions as “.jpg,.png”.
703
- *
704
- * @default ''
705
- */
706
- @Property('')
707
- public allowedExtensions: string;
708
-
709
- /**
710
- * Specifies the minimum file size to be uploaded in bytes.
711
- * The property used to make sure that you cannot upload empty files and small files.
712
- *
713
- * @default 0
714
- */
715
- @Property(0)
716
- public minFileSize: number;
717
-
718
- /**
719
- * Specifies the maximum allowed file size to be uploaded in bytes.
720
- * The property used to make sure that you cannot upload too large files.
721
- *
722
- * @default 30000000
723
- */
724
- @Property(30000000)
725
- public maxFileSize: number;
726
-
727
- /**
728
- * Specifies the drop target to handle the drag-and-drop upload.
729
- * By default, the component creates wrapper around file input that will act as drop target.
730
- *
731
- * > For more information, refer to the [drag-and-drop](../../uploader/file-source/#drag-and-drop) section from the documentation.
732
- *
733
- * @default null
734
- */
735
- @Property(null)
736
- public dropArea: string | HTMLElement;
737
-
738
- /**
739
- * Specifies the list of files that will be preloaded on rendering of uploader component.
740
- * The property used to view and remove the uploaded files from server. By default, the files are configured with
741
- * uploaded successfully state. The following properties are mandatory to configure the preload files:
742
- * * Name
743
- * * Size
744
- * * Type
745
- *
746
- * {% codeBlock src='uploader/files/index.md' %}{% endcodeBlock %}
747
- *
748
- * @default { name: '', size: null, type: '' }
749
- */
750
- @Collection<FilesPropModel>([{}], FilesProp)
751
- public files: FilesPropModel[];
752
-
753
- /**
754
- * Specifies a Boolean value that indicates whether the default file list can be rendered.
755
- * The property used to prevent default file list and design own template for file list.
756
- *
757
- * @default true
758
- */
759
- @Property(true)
760
- public showFileList: boolean;
761
-
762
- /**
763
- * Specifies a Boolean value that indicates whether the folder of files can be browsed in the uploader component.
764
- *
765
- * > When enabled this property, it allows only files of folder to select or drop to upload and
766
- * it cannot be allowed to select or drop files.
767
- *
768
- * @default false
769
- */
770
- @Property(false)
771
- public directoryUpload: boolean;
772
-
773
- /**
774
- * Specifies the drag operation effect to the uploader component. Possible values are Copy , Move, Link and None.
775
- *
776
- * By default, the uploader component works based on the browser drag operation effect.
777
- *
778
- * @default 'Default'
779
- */
780
- @Property('Default')
781
- public dropEffect: DropEffect;
782
-
783
- /**
784
- * Triggers when the component is created.
785
- *
786
- * @event created
787
- */
788
- @Event()
789
- public created: EmitType<Object>;
790
-
791
- /**
792
- * Triggers after all the selected files has processed to upload successfully or failed to server.
793
- *
794
- * @event actionComplete
795
- */
796
- @Event()
797
- public actionComplete: EmitType<ActionCompleteEventArgs>;
798
-
799
- /**
800
- * DEPRECATED-Triggers before rendering each file item from the file list in a page.
801
- * It helps to customize specific file item structure.
802
- *
803
- * @event rendering
804
- */
805
- @Event()
806
- public rendering: EmitType<RenderingEventArgs>;
807
-
808
- /**
809
- * Triggers when the upload process before. This event is used to add additional parameter with upload request.
810
- *
811
- * @event beforeUpload
812
- */
813
- @Event()
814
- public beforeUpload: EmitType<BeforeUploadEventArgs>;
815
-
816
- /**
817
- * Triggers before rendering each file item from the file list in a page.
818
- * It helps to customize specific file item structure.
819
- *
820
- * @event fileListRendering
821
- */
822
- @Event()
823
- public fileListRendering: EmitType<FileListRenderingEventArgs>;
824
-
825
- /**
826
- * Triggers after selecting or dropping the files by adding the files in upload queue.
827
- *
828
- * @event selected
829
- */
830
- @Event()
831
- public selected: EmitType<SelectedEventArgs>;
832
-
833
- /**
834
- * Triggers when the upload process gets started. This event is used to add additional parameter with upload request.
835
- *
836
- * @event uploading
837
- */
838
- @Event()
839
- public uploading: EmitType<UploadingEventArgs>;
840
-
841
- /**
842
- * Triggers when the AJAX request gets success on uploading files or removing files.
843
- *
844
- * <table>
845
- * <tr>
846
- * <td colSpan=1 rowSpan=1>
847
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
848
- * Description<br/></td></tr>
849
- * <tr>
850
- * <td colSpan=1 rowSpan=1>
851
- * event<br/></td><td colSpan=1 rowSpan=1>
852
- * Ajax progress event arguments.<br/></td></tr>
853
- * <tr>
854
- * <td colSpan=1 rowSpan=1>
855
- * file<br/></td><td colSpan=1 rowSpan=1>
856
- * File information which is uploaded/removed.<br/></td></tr>
857
- * <tr>
858
- * <td colSpan=1 rowSpan=1>
859
- * name<br/></td><td colSpan=1 rowSpan=1>
860
- * Name of the event<br/></td></tr>
861
- * <tr>
862
- * <td colSpan=1 rowSpan=1>
863
- * operation<br/></td><td colSpan=1 rowSpan=1>
864
- * It indicates the success of the operation whether its uploaded or removed<br/></td></tr>
865
- * </table>
866
- *
867
- * @event success
868
- */
869
- @Event()
870
- public success: EmitType<Object>;
871
-
872
- /**
873
- * Triggers when the AJAX request fails on uploading or removing files.
874
- *
875
- * <table>
876
- * <tr>
877
- * <td colSpan=1 rowSpan=1>
878
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
879
- * Description<br/></td></tr>
880
- * <tr>
881
- * <td colSpan=1 rowSpan=1>
882
- * event<br/></td><td colSpan=1 rowSpan=1>
883
- * Ajax progress event arguments.<br/></td></tr>
884
- * <tr>
885
- * <td colSpan=1 rowSpan=1>
886
- * file<br/></td><td colSpan=1 rowSpan=1>
887
- * File information which is failed from upload/remove.<br/></td></tr>
888
- * <tr>
889
- * <td colSpan=1 rowSpan=1>
890
- * name<br/></td><td colSpan=1 rowSpan=1>
891
- * Name of the event<br/></td></tr>
892
- * <tr>
893
- * <td colSpan=1 rowSpan=1>
894
- * operation<br/></td><td colSpan=1 rowSpan=1>
895
- * It indicates the failure of the operation whether its upload or remove<br/></td></tr>
896
- * </table>
897
- *
898
- * @event failure
899
- */
900
- @Event()
901
- public failure: EmitType<Object>;
902
-
903
- /**
904
- * Triggers on removing the uploaded file. The event used to get confirm before removing the file from server.
905
- *
906
- * @event removing
907
- */
908
- @Event()
909
- public removing: EmitType<RemovingEventArgs>;
910
-
911
- /**
912
- * Triggers on remove the uploaded file. The event used to get confirm before remove the file from server.
913
- *
914
- * @event beforeRemove
915
- */
916
- @Event()
917
- public beforeRemove: EmitType<BeforeRemoveEventArgs>;
918
-
919
- /**
920
- * Triggers before clearing the items in file list when clicking “clear”.
921
- *
922
- * @event clearing
923
- */
924
- @Event()
925
- public clearing: EmitType<ClearingEventArgs>;
926
-
927
- /**
928
- * Triggers when uploading a file to the server using the AJAX request.
929
- *
930
- * <table>
931
- * <tr>
932
- * <td colSpan=1 rowSpan=1>
933
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
934
- * Description<br/></td></tr>
935
- * <tr>
936
- * <td colSpan=1 rowSpan=1>
937
- * event<br/></td><td colSpan=1 rowSpan=1>
938
- * Ajax progress event arguments.<br/></td></tr>
939
- * <tr>
940
- * <td colSpan=1 rowSpan=1>
941
- * file<br/></td><td colSpan=1 rowSpan=1>
942
- * File information which is uploading to server.<br/></td></tr>
943
- * <tr>
944
- * <td colSpan=1 rowSpan=1>
945
- * name<br/></td><td colSpan=1 rowSpan=1>
946
- * Name of the event<br/></td></tr>
947
- * </table>
948
- *
949
- * @event progress
950
- */
951
- @Event()
952
- public progress: EmitType<Object>;
953
-
954
- /**
955
- * Triggers when changes occur in uploaded file list by selecting or dropping files.
956
- *
957
- * <table>
958
- * <tr>
959
- * <td colSpan=1 rowSpan=1>
960
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
961
- * Description<br/></td></tr>
962
- * <tr>
963
- * <td colSpan=1 rowSpan=1>
964
- * file<br/></td><td colSpan=1 rowSpan=1>
965
- * File information which is successfully uploaded to server or removed in server.<br/></td></tr>
966
- * <tr>
967
- * <td colSpan=1 rowSpan=1>
968
- * name<br/></td><td colSpan=1 rowSpan=1>
969
- * Name of the event<br/></td></tr>
970
- * </table>
971
- *
972
- * @event change
973
- */
974
- @Event()
975
- public change: EmitType<Object>;
976
-
977
- /**
978
- * Fires when the chunk file uploaded successfully.
979
- *
980
- * <table>
981
- * <tr>
982
- * <td colSpan=1 rowSpan=1>
983
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
984
- * Description<br/></td></tr>
985
- * <tr>
986
- * <td colSpan=1 rowSpan=1>
987
- * chunkIndex<br/></td><td colSpan=1 rowSpan=1>
988
- * Returns current chunk index.<br/></td></tr>
989
- * <tr>
990
- * <td colSpan=1 rowSpan=1>
991
- * chunkSize<br/></td><td colSpan=1 rowSpan=1>
992
- * Returns the size of the chunk file.<br/></td></tr>
993
- * <tr>
994
- * <td colSpan=1 rowSpan=1>
995
- * file<br/></td><td colSpan=1 rowSpan=1>
996
- * File information which is uploading to server.<br/></td></tr>
997
- * <tr>
998
- * <td colSpan=1 rowSpan=1>
999
- * name<br/></td><td colSpan=1 rowSpan=1>
1000
- * Name of the event<br/></td></tr>
1001
- * </table>
1002
- *
1003
- * @event chunkSuccess
1004
- */
1005
- @Event()
1006
- public chunkSuccess: EmitType<Object>;
1007
-
1008
- /**
1009
- * Fires if the chunk file failed to upload.
1010
- *
1011
- * <table>
1012
- * <tr>
1013
- * <td colSpan=1 rowSpan=1>
1014
- * Event arguments<br/></td><td colSpan=1 rowSpan=1>
1015
- * Description<br/></td></tr>
1016
- * <tr>
1017
- * <td colSpan=1 rowSpan=1>
1018
- * chunkIndex<br/></td><td colSpan=1 rowSpan=1>
1019
- * Returns current chunk index.<br/></td></tr>
1020
- * <tr>
1021
- * <td colSpan=1 rowSpan=1>
1022
- * chunkSize<br/></td><td colSpan=1 rowSpan=1>
1023
- * Returns the size of the chunk file.<br/></td></tr>
1024
- * <tr>
1025
- * <td colSpan=1 rowSpan=1>
1026
- * file<br/></td><td colSpan=1 rowSpan=1>
1027
- * File information which is uploading to server.<br/></td></tr>
1028
- * <tr>
1029
- * <td colSpan=1 rowSpan=1>
1030
- * name<br/></td><td colSpan=1 rowSpan=1>
1031
- * Name of the event<br/></td></tr>
1032
- * <tr>
1033
- * <td colSpan=1 rowSpan=1>
1034
- * totalChunk<br/></td><td colSpan=1 rowSpan=1>
1035
- * Returns the total chunk count<br/></td></tr>
1036
- * <tr>
1037
- * <td colSpan=1 rowSpan=1>
1038
- * cancel<br/></td><td colSpan=1 rowSpan=1>
1039
- * Prevent triggering of failure event when we pass true to this attribute<br/></td></tr>
1040
- * </table>
1041
- *
1042
- * @event chunkFailure
1043
- */
1044
- @Event()
1045
- public chunkFailure: EmitType<Object>;
1046
-
1047
- /**
1048
- * Fires when every chunk upload process gets started. This event is used to add additional parameter with upload request.
1049
- *
1050
- * @event chunkUploading
1051
- */
1052
- @Event()
1053
- public chunkUploading: EmitType<UploadingEventArgs>;
1054
-
1055
- /**
1056
- * Fires if cancel the chunk file uploading.
1057
- *
1058
- * @event canceling
1059
- */
1060
- @Event()
1061
- public canceling: EmitType<CancelEventArgs>;
1062
-
1063
- /**
1064
- * Fires if pause the chunk file uploading.
1065
- *
1066
- * @event pausing
1067
- */
1068
- @Event()
1069
- public pausing: EmitType<PauseResumeEventArgs>;
1070
-
1071
- /**
1072
- * Fires if resume the paused chunk file upload.
1073
- *
1074
- * @event resuming
1075
- */
1076
- @Event()
1077
- public resuming: EmitType<PauseResumeEventArgs>;
1078
- /**
1079
- * Triggers when change the Uploader value.
1080
- *
1081
- * @param {UploaderModel} options - Specifies the Uploader model.
1082
- * @param {string | HTMLInputElement} element - Specifies the element to render as component.
1083
- * @private
1084
- */
1085
- public constructor(options?: UploaderModel, element?: string | HTMLInputElement) {
1086
- super(options, element);
1087
- this.uploaderOptions = options;
1088
- }
1089
-
1090
- /**
1091
- * Calls internally if any of the property value is changed.
1092
- *
1093
- * @param {UploaderModel} newProp - Returns the dynamic property value of the component.
1094
- * @param {UploaderModel} oldProp - Returns the previous property value of the component.
1095
- * @returns {void}
1096
- * @private
1097
- */
1098
- public onPropertyChanged(newProp: UploaderModel, oldProp: UploaderModel): void {
1099
- for (const prop of Object.keys(newProp)) {
1100
- switch (prop) {
1101
- case 'allowedExtensions':
1102
- this.setExtensions(this.allowedExtensions);
1103
- this.clearAll();
1104
- break;
1105
- case 'enabled':
1106
- this.setControlStatus();
1107
- break;
1108
- case 'multiple':
1109
- this.setMultipleSelection();
1110
- break;
1111
- case 'enableRtl':
1112
- this.setRTL();
1113
- this.reRenderFileList();
1114
- break;
1115
- case 'buttons':
1116
- this.buttons.browse = isNullOrUndefined(this.buttons.browse) ? '' : this.buttons.browse;
1117
- this.buttons.clear = isNullOrUndefined(this.buttons.clear) ? '' : this.buttons.clear;
1118
- this.buttons.upload = isNullOrUndefined(this.buttons.upload) ? '' : this.buttons.upload;
1119
- this.renderButtonTemplates();
1120
- break;
1121
- case 'dropArea':
1122
- this.unBindDropEvents();
1123
- this.updateDropArea();
1124
- break;
1125
- case 'htmlAttributes':
1126
- this.updateHTMLAttrToElement();
1127
- this.updateHTMLAttrToWrapper();
1128
- this.checkHTMLAttributes(true);
1129
- break;
1130
- case 'files':
1131
- this.renderPreLoadFiles();
1132
- break;
1133
- case 'directoryUpload':
1134
- this.updateDirectoryAttributes();
1135
- break;
1136
- case 'template':
1137
- if ((this as any).isReact){
1138
- this.reRenderFileList();
1139
- }else{
1140
- this.clearAll();
1141
- }
1142
- break;
1143
- case 'minFileSize':
1144
- case 'maxFileSize':
1145
- case 'autoUpload':
1146
- this.clearAll();
1147
- break;
1148
- case 'sequentialUpload':
1149
- this.clearAll();
1150
- break;
1151
- case 'locale':
1152
- this.l10n.setLocale(this.locale);
1153
- this.setLocalizedTexts();
1154
- this.preLocaleObj = getValue('currentLocale', this.l10n);
1155
- break;
1156
- case 'cssClass':
1157
- this.setCSSClass(oldProp.cssClass);
1158
- break;
1159
- }
1160
- }
1161
- }
1162
-
1163
- private setLocalizedTexts() : void {
1164
- if (isNullOrUndefined(this.template)) {
1165
- if (typeof (this.buttons.browse) === 'string') {
1166
- this.browseButton.innerText = (this.buttons.browse === 'Browse...') ?
1167
- this.localizedTexts('Browse') : this.buttons.browse;
1168
- this.browseButton.setAttribute('title', this.browseButton.innerText);
1169
- if (this.uploadWrapper && !isNullOrUndefined(this.uploadWrapper.querySelector('.' + DROP_AREA))) {
1170
- this.uploadWrapper.querySelector('.' + DROP_AREA).innerHTML = this.localizedTexts('dropFilesHint');
1171
- }
1172
- }
1173
- this.updateFileList();
1174
- }
1175
- }
1176
-
1177
- private getKeyValue(val : string): string {
1178
- let keyValue: string;
1179
- for (const key of Object.keys(this.preLocaleObj)) {
1180
- if (this.preLocaleObj[`${key}`] === val) {
1181
- keyValue = key;
1182
- }
1183
- }
1184
- return keyValue;
1185
- }
1186
-
1187
- private updateFileList () : void {
1188
- let element : HTMLElement;
1189
- /* istanbul ignore next */
1190
- if (this.fileList.length > 0 && !isNullOrUndefined(this.uploadWrapper.querySelector('.' + LIST_PARENT ))) {
1191
- for (let i: number = 0; i < this.fileList.length; i++) {
1192
- element = this.fileList[i as number].querySelector('.e-file-status') as HTMLElement;
1193
- element.innerHTML = this.localizedTexts(this.getKeyValue(this.filesData[i as number].status));
1194
- this.filesData[i as number].status = this.localizedTexts(this.getKeyValue(this.filesData[i as number].status));
1195
- if (this.fileList[i as number].classList.contains(UPLOAD_SUCCESS)) {
1196
- this.fileList[i as number].querySelector('.e-icons').setAttribute('title', this.localizedTexts('delete'));
1197
- }
1198
- if (this.fileList[i as number].querySelector('.e-file-play-btn')) {
1199
- this.fileList[i as number].querySelector('.e-icons').setAttribute('title', this.localizedTexts('resume'));
1200
- }
1201
- if (this.fileList[i as number].querySelector('.e-file-remove-btn')) {
1202
- this.fileList[i as number].querySelector('.e-icons').setAttribute('title', this.localizedTexts('remove'));
1203
- }
1204
- if (this.fileList[i as number].querySelector('.e-file-reload-btn')) {
1205
- this.fileList[i as number].querySelector('.e-icons').setAttribute('title', this.localizedTexts('retry'));
1206
- }
1207
- if (!this.autoUpload) {
1208
- this.uploadButton.innerText = (this.buttons.upload === 'Upload') ?
1209
- this.localizedTexts('Upload') : <string>this.buttons.upload;
1210
- this.uploadButton.setAttribute('title', this.localizedTexts('Upload'));
1211
- this.clearButton.innerText = (this.buttons.clear === 'Clear') ?
1212
- this.localizedTexts('Clear') : <string>this.buttons.clear;
1213
- this.clearButton.setAttribute('title', this.localizedTexts('Clear'));
1214
- }
1215
- }
1216
- }
1217
- }
1218
-
1219
- private reRenderFileList(): void {
1220
- if (this.listParent) {
1221
- detach(this.listParent);
1222
- this.listParent = null;
1223
- this.fileList = [];
1224
- this.internalCreateFileList(this.filesData);
1225
- if (this.actionButtons) {
1226
- this.removeActionButtons();
1227
- this.renderActionButtons();
1228
- this.checkActionButtonStatus();
1229
- }
1230
- }
1231
- }
1232
-
1233
- protected preRender(): void {
1234
- this.localeText = { Browse : 'Browse...', Clear : 'Clear', Upload : 'Upload', invalidFileName : 'File Name is not allowed',
1235
- dropFilesHint : 'Or drop files here', invalidMaxFileSize : 'File size is too large',
1236
- invalidMinFileSize : 'File size is too small', invalidFileType: 'File type is not allowed',
1237
- uploadFailedMessage : 'File failed to upload', uploadSuccessMessage : 'File uploaded successfully',
1238
- removedSuccessMessage: 'File removed successfully', removedFailedMessage: 'Unable to remove file', inProgress: 'Uploading',
1239
- readyToUploadMessage: 'Ready to upload', abort: 'Abort', remove: 'Remove', cancel: 'Cancel', delete: 'Delete file',
1240
- pauseUpload: 'File upload paused', pause: 'Pause', resume: 'Resume', retry: 'Retry',
1241
- fileUploadCancel: 'File upload canceled', invalidFileSelection: 'Invalid files selected', totalFiles: 'Total files',
1242
- size: 'Size'
1243
- };
1244
- this.l10n = new L10n('uploader', this.localeText, this.locale);
1245
- this.preLocaleObj = getValue('currentLocale', this.l10n);
1246
- this.formRendered();
1247
- this.updateHTMLAttrToElement();
1248
- this.checkHTMLAttributes(false);
1249
- const ejInstance: any = getValue('ej2_instances', this.element);
1250
- /* istanbul ignore next */
1251
- if (this.element.tagName === 'EJS-UPLOADER') {
1252
- const inputElement: HTMLInputElement = <HTMLInputElement>this.createElement('input', { attrs: { type: 'file' }});
1253
- let index: number = 0;
1254
- for (index; index < this.element.attributes.length; index++) {
1255
- if (this.element.attributes[index as number].nodeName !== 'id'){
1256
- inputElement.setAttribute(this.element.attributes[index as number].nodeName,
1257
- this.element.attributes[index as number].nodeValue);
1258
- }
1259
- else if (this.element.attributes[index as number].nodeName === 'id'){
1260
- inputElement.setAttribute(this.element.attributes[index as number].nodeName, getUniqueID('uploader'));
1261
- }
1262
- inputElement.innerHTML = this.element.innerHTML;
1263
- }
1264
- if (!inputElement.hasAttribute('name')) {
1265
- inputElement.setAttribute('name', 'UploadFiles');
1266
- }
1267
- this.element.appendChild(inputElement);
1268
- this.element = inputElement;
1269
- setValue('ej2_instances', ejInstance, this.element);
1270
- }
1271
- /* istanbul ignore next */
1272
- if (ejInstance[0].isPureReactComponent) {
1273
- if (!isNullOrUndefined(ejInstance[0].props.name)) {
1274
- this.element.setAttribute('name', ejInstance[0].props.name);
1275
- } else if (!isNullOrUndefined(ejInstance[0].props.id) && isNullOrUndefined(ejInstance[0].props.name)) {
1276
- this.element.setAttribute('name', ejInstance[0].props.id);
1277
- } else {
1278
- this.element.setAttribute('name', 'UploadFiles');
1279
- }
1280
- }
1281
- if (isNullOrUndefined(this.element.getAttribute('name'))) {
1282
- this.element.setAttribute('name', this.element.getAttribute('id'));
1283
- }
1284
- if (!this.element.hasAttribute('type')) {
1285
- this.element.setAttribute('type', 'file');
1286
- }
1287
- this.updateDirectoryAttributes();
1288
- this.keyConfigs = {
1289
- enter: 'enter'
1290
- };
1291
- if (this.element.hasAttribute('tabindex')) {
1292
- this.tabIndex = this.element.getAttribute('tabindex');
1293
- }
1294
- this.browserName = Browser.info.name;
1295
- this.uploaderName = this.element.getAttribute('name');
1296
- }
1297
-
1298
- private formRendered(): void {
1299
- let parentEle: HTMLElement = closest(this.element, 'form') as HTMLElement;
1300
- if (!isNullOrUndefined(parentEle)) {
1301
- for (; parentEle && parentEle !== document.documentElement; parentEle = parentEle.parentElement) {
1302
- if (parentEle.tagName === 'FORM') {
1303
- this.isForm = true;
1304
- this.formElement = parentEle;
1305
- parentEle.setAttribute('enctype', 'multipart/form-data');
1306
- parentEle.setAttribute('encoding', 'multipart/form-data');
1307
- }
1308
- }
1309
- }
1310
- }
1311
-
1312
- protected getPersistData(): string {
1313
- return this.addOnPersist(['filesData']);
1314
- }
1315
-
1316
- /**
1317
- * Return the module name of the component.
1318
- *
1319
- * @returns {string} Returns the component name.
1320
- */
1321
- public getModuleName(): string {
1322
- return 'uploader';
1323
- }
1324
-
1325
- private updateDirectoryAttributes(): void {
1326
- if (this.directoryUpload) {
1327
- this.element.setAttribute('directory', 'true');
1328
- this.element.setAttribute('webkitdirectory', 'true');
1329
- } else {
1330
- this.element.removeAttribute('directory');
1331
- this.element.removeAttribute('webkitdirectory');
1332
- }
1333
- }
1334
-
1335
- /**
1336
- * To Initialize the control rendering
1337
- *
1338
- * @private
1339
- * @returns {void}
1340
- */
1341
- public render(): void {
1342
- this.renderBrowseButton();
1343
- this.initializeUpload();
1344
- this.updateHTMLAttrToWrapper();
1345
- this.wireEvents();
1346
- this.setMultipleSelection();
1347
- this.setExtensions(this.allowedExtensions);
1348
- this.setRTL();
1349
- this.renderPreLoadFiles();
1350
- this.setControlStatus();
1351
- this.setCSSClass();
1352
- }
1353
-
1354
- private renderBrowseButton(): void {
1355
- this.browseButton = this.createElement('button', { className: 'e-css e-btn', attrs: {'type': 'button'}});
1356
- this.browseButton.setAttribute('tabindex', this.tabIndex);
1357
- if (typeof(this.buttons.browse) === 'string') {
1358
- this.browseButton.textContent = (this.buttons.browse === 'Browse...') ?
1359
- this.localizedTexts('Browse') : this.buttons.browse;
1360
- this.browseButton.setAttribute('title', this.browseButton.innerText);
1361
- } else {
1362
- this.browseButton.appendChild(this.buttons.browse);
1363
- }
1364
- this.element.setAttribute('aria-label', 'Uploader');
1365
- }
1366
-
1367
- private renderActionButtons(): void {
1368
- this.element.setAttribute('tabindex', '-1');
1369
- this.actionButtons = this.createElement('div', { className: ACTION_BUTTONS });
1370
- this.uploadButton = this.createElement('button', { className: UPLOAD_BUTTONS ,
1371
- attrs: {'type': 'button', 'tabindex': this.btnTabIndex , 'aria-label': this.localizedTexts('Upload')} });
1372
- this.clearButton = this.createElement('button', { className: CLEAR_BUTTONS,
1373
- attrs: {'type': 'button', 'tabindex': this.btnTabIndex, 'aria-label': this.localizedTexts('Clear') } });
1374
- this.actionButtons.appendChild(this.clearButton);
1375
- this.actionButtons.appendChild(this.uploadButton);
1376
- this.renderButtonTemplates();
1377
- this.uploadWrapper.appendChild(this.actionButtons);
1378
- this.browseButton.blur();
1379
- if (!this.isPreloadFiles) {
1380
- this.uploadButton.focus();
1381
- }
1382
- this.wireActionButtonEvents();
1383
- }
1384
-
1385
- /* istanbul ignore next */
1386
- private serverActionButtonsEventBind(element: HTMLElement): void {
1387
- if (element && !this.isForm) {
1388
- this.browseButton.blur();
1389
- this.actionButtons = element;
1390
- this.uploadButton = this.actionButtons.querySelector('.e-file-upload-btn');
1391
- this.clearButton = this.actionButtons.querySelector('.e-file-clear-btn');
1392
- this.uploadButton.focus();
1393
- this.unwireActionButtonEvents();
1394
- this.wireActionButtonEvents();
1395
- this.checkActionButtonStatus();
1396
- }
1397
- }
1398
-
1399
- private wireActionButtonEvents(): void {
1400
- EventHandler.add(this.uploadButton, 'click', this.uploadButtonClick, this);
1401
- EventHandler.add(this.clearButton, 'click', this.clearButtonClick, this);
1402
- }
1403
-
1404
- private unwireActionButtonEvents(): void {
1405
- EventHandler.remove(this.uploadButton, 'click', this.uploadButtonClick);
1406
- EventHandler.remove(this.clearButton, 'click', this.clearButtonClick);
1407
- }
1408
-
1409
- private removeActionButtons(): void {
1410
- if (this.actionButtons) {
1411
- this.unwireActionButtonEvents();
1412
- detach(this.actionButtons);
1413
- this.actionButtons = null;
1414
- }
1415
- }
1416
-
1417
- private renderButtonTemplates(): void {
1418
- if (typeof (this.buttons.browse) === 'string') {
1419
- this.browseButton.textContent = (this.buttons.browse === 'Browse...') ?
1420
- this.localizedTexts('Browse') : this.buttons.browse;
1421
- this.browseButton.setAttribute('title', this.browseButton.textContent);
1422
- } else {
1423
- this.browseButton.innerHTML = '';
1424
- this.browseButton.appendChild(this.buttons.browse);
1425
- }
1426
- if (this.uploadButton) {
1427
- const uploadText: string | HTMLElement = isNullOrUndefined(this.buttons.upload) ? 'Upload' : this.buttons.upload;
1428
- this.buttons.upload = uploadText;
1429
- if (typeof (this.buttons.upload) === 'string') {
1430
- this.uploadButton.textContent = (this.buttons.upload === 'Upload') ?
1431
- this.localizedTexts('Upload') : this.buttons.upload;
1432
- this.uploadButton.setAttribute('title', this.uploadButton.textContent);
1433
- } else {
1434
- this.uploadButton.innerHTML = '';
1435
- this.uploadButton.appendChild(this.buttons.upload);
1436
- }
1437
- }
1438
- if (this.clearButton) {
1439
- const clearText: string | HTMLElement = isNullOrUndefined(this.buttons.clear) ? 'Clear' : this.buttons.clear;
1440
- this.buttons.clear = clearText;
1441
- if (typeof (this.buttons.clear) === 'string') {
1442
- this.clearButton.textContent = (this.buttons.clear === 'Clear') ?
1443
- this.localizedTexts('Clear') : this.buttons.clear;
1444
- this.clearButton.setAttribute('title', this.clearButton.textContent);
1445
- } else {
1446
- this.clearButton.innerHTML = '';
1447
- this.clearButton.appendChild(this.buttons.clear);
1448
- }
1449
- }
1450
- }
1451
-
1452
- private initializeUpload(): void {
1453
- this.element.setAttribute('tabindex', '-1');
1454
- const inputWrapper: HTMLElement = this.createElement('span', { className: INPUT_WRAPPER });
1455
- this.element.parentElement.insertBefore(inputWrapper, this.element);
1456
- this.dropAreaWrapper = this.createElement('div', { className: DROP_WRAPPER });
1457
- this.element.parentElement.insertBefore(this.dropAreaWrapper, this.element);
1458
- inputWrapper.appendChild(this.element);
1459
- this.dropAreaWrapper.appendChild(this.browseButton);
1460
- this.dropAreaWrapper.appendChild(inputWrapper);
1461
- this.uploadWrapper = this.createElement('div', { className: CONTROL_WRAPPER });
1462
- this.dropAreaWrapper.parentElement.insertBefore(this.uploadWrapper, this.dropAreaWrapper);
1463
- this.uploadWrapper.appendChild(this.dropAreaWrapper);
1464
- this.setDropArea();
1465
- }
1466
-
1467
- private renderPreLoadFiles(): void {
1468
- if (this.files.length) {
1469
- if (this.enablePersistence && this.filesData.length) {
1470
- this.internalCreateFileList(this.filesData);
1471
- return;
1472
- }
1473
- if (isNullOrUndefined(this.files[0].size)) {
1474
- return;
1475
- }
1476
- this.isPreloadFiles = true;
1477
- let files: FilesPropModel[] = [].slice.call(this.files);
1478
- const filesData: FileInfo[] = [];
1479
- if (!this.multiple) {
1480
- this.clearData();
1481
- files = [files[0]];
1482
- }
1483
- for (const data of files) {
1484
- const fileData: FileInfo = {
1485
- name: data.name + '.' + data.type.split('.')[data.type.split('.').length - 1],
1486
- rawFile: '',
1487
- size: data.size,
1488
- status: this.localizedTexts('uploadSuccessMessage'),
1489
- type: data.type,
1490
- validationMessages: {minSize: '', maxSize: ''},
1491
- statusCode: '2'
1492
- };
1493
- filesData.push(fileData);
1494
- this.filesData.push(fileData);
1495
- }
1496
- this.internalCreateFileList(filesData);
1497
- if (!this.autoUpload && this.listParent && !this.actionButtons && (!this.isForm || this.allowUpload()) && this.showFileList) {
1498
- this.renderActionButtons();
1499
- }
1500
- this.checkActionButtonStatus();
1501
- if (this.sequentialUpload) {
1502
- this.count = this.filesData.length - 1;
1503
- }
1504
- this.isPreloadFiles = false;
1505
- }
1506
- }
1507
-
1508
- private checkActionButtonStatus(): void {
1509
- if (this.actionButtons) {
1510
- const length: number = this.uploadWrapper.querySelectorAll('.' + VALIDATION_FAILS).length +
1511
- this.uploadWrapper.querySelectorAll('.e-upload-fails:not(.e-upload-progress)').length +
1512
- this.uploadWrapper.querySelectorAll('span.' + UPLOAD_SUCCESS).length +
1513
- this.uploadWrapper.querySelectorAll('span.' + UPLOAD_INPROGRESS).length;
1514
- if (length > 0 && length === this.uploadWrapper.querySelectorAll('li').length) {
1515
- this.uploadButton.setAttribute('disabled', 'disabled');
1516
- } else {
1517
- this.uploadButton.removeAttribute('disabled');
1518
- }
1519
- }
1520
- }
1521
-
1522
- private setDropArea(): void {
1523
- const dropTextArea: HTMLElement = <HTMLElement>this.dropAreaWrapper.querySelector('.e-file-drop');
1524
- if (this.dropArea) {
1525
- this.dropZoneElement = (typeof(this.dropArea) !== 'string') ? this.dropArea :
1526
- select(this.dropArea, document) as HTMLElement;
1527
- let element: HTMLElement = this.element;
1528
- let enableDropText: boolean = false;
1529
- while (element.parentNode) {
1530
- element = element.parentNode as HTMLElement;
1531
- if (element === this.dropZoneElement) {
1532
- enableDropText = true;
1533
- if (!dropTextArea) {
1534
- this.createDropTextHint();
1535
- } else {
1536
- dropTextArea.innerHTML = this.localizedTexts('dropFilesHint');
1537
- }
1538
- }
1539
- }
1540
- if (!enableDropText && dropTextArea) {
1541
- remove(dropTextArea);
1542
- }
1543
- } else if (!isNullOrUndefined(this.uploaderOptions) && this.uploaderOptions.dropArea === undefined) {
1544
- this.createDropTextHint();
1545
- this.dropZoneElement = this.uploadWrapper;
1546
- this.setProperties({dropArea: this.uploadWrapper }, true);
1547
- }
1548
- this.bindDropEvents();
1549
- }
1550
-
1551
- private updateDropArea() : void {
1552
- if (this.dropArea) {
1553
- this.setDropArea();
1554
- } else {
1555
- this.dropZoneElement = null;
1556
- const dropTextArea: HTMLElement = <HTMLElement>this.dropAreaWrapper.querySelector('.e-file-drop');
1557
- if (dropTextArea) {
1558
- remove(dropTextArea);
1559
- }
1560
- }
1561
- }
1562
-
1563
- private createDropTextHint () : void {
1564
- const fileDropArea: HTMLElement = this.createElement('span', { className: DROP_AREA});
1565
- fileDropArea.innerHTML = this.localizedTexts('dropFilesHint');
1566
- this.dropAreaWrapper.appendChild(fileDropArea);
1567
- }
1568
-
1569
- private updateHTMLAttrToElement(): void {
1570
- if ( !isNullOrUndefined(this.htmlAttributes)) {
1571
- for (const pro of Object.keys(this.htmlAttributes)) {
1572
- if (wrapperAttr.indexOf(pro) < 0 ) {
1573
- this.element.setAttribute(pro, this.htmlAttributes[`${pro}`]);
1574
- }
1575
- }
1576
- }
1577
- }
1578
- private updateHTMLAttrToWrapper(): void {
1579
- if ( !isNullOrUndefined(this.htmlAttributes)) {
1580
- for (const pro of Object.keys(this.htmlAttributes)) {
1581
- if (wrapperAttr.indexOf(pro) > -1 ) {
1582
- if (pro === 'class') {
1583
- const updatedClassValues : string = (this.htmlAttributes[`${pro}`].replace(/\s+/g, ' ')).trim();
1584
- if (updatedClassValues !== '') {
1585
- addClass([this.uploadWrapper], updatedClassValues.split(' '));
1586
- }
1587
- } else if (pro === 'style') {
1588
- let uploadStyle: string = this.uploadWrapper.getAttribute(pro);
1589
- uploadStyle = !isNullOrUndefined(uploadStyle) ? (uploadStyle + this.htmlAttributes[`${pro}`]) :
1590
- this.htmlAttributes[`${pro}`];
1591
- this.uploadWrapper.setAttribute(pro, uploadStyle);
1592
- } else {
1593
- this.uploadWrapper.setAttribute(pro, this.htmlAttributes[`${pro}`]);
1594
- }
1595
- }
1596
- }
1597
- }
1598
- }
1599
-
1600
- private setMultipleSelection(): void {
1601
- if (this.multiple && !this.element.hasAttribute('multiple')) {
1602
- const newAttr: Attr = document.createAttribute('multiple');
1603
- newAttr.value = 'multiple';
1604
- this.element.setAttributeNode(newAttr);
1605
- } else if (!this.multiple) {
1606
- this.element.removeAttribute('multiple');
1607
- }
1608
- }
1609
-
1610
- private checkAutoUpload(fileData: FileInfo[]): void {
1611
- if (this.autoUpload) {
1612
- if (this.sequentialUpload) {
1613
- /* istanbul ignore next */
1614
- this.sequenceUpload(fileData);
1615
- } else {
1616
- this.upload(fileData);
1617
- }
1618
- this.removeActionButtons();
1619
- } else if (!this.actionButtons) {
1620
- this.renderActionButtons();
1621
- }
1622
- this.checkActionButtonStatus();
1623
- }
1624
-
1625
- private sequenceUpload(fileData: FileInfo[]): void {
1626
- if ( this.filesData.length - fileData.length === 0 ||
1627
- this.filesData[(this.filesData.length - fileData.length - 1)].statusCode !== '1' ) {
1628
- if (this.multiple || this.count < 0){
1629
- ++this.count;
1630
- }
1631
- const isFileListCreated: boolean = this.showFileList ? false : true;
1632
- if (typeof this.filesData[this.count] === 'object') {
1633
- this.isFirstFileOnSelection = false;
1634
- this.upload(this.filesData[this.count], isFileListCreated);
1635
- if (this.filesData[this.count].statusCode === '0') {
1636
- this.sequenceUpload(fileData);
1637
- }
1638
- } else {
1639
- --this.count;
1640
- }
1641
- }
1642
- }
1643
- private setCSSClass(oldCSSClass?: string): void {
1644
- let updatedOldCssClass: string = oldCSSClass;
1645
- if (!isNullOrUndefined(oldCSSClass)) {
1646
- updatedOldCssClass = (oldCSSClass.replace(/\s+/g, ' ')).trim();
1647
- }
1648
- if (!isNullOrUndefined(oldCSSClass) && updatedOldCssClass !== '') {
1649
- removeClass([this.uploadWrapper], updatedOldCssClass.split(' '));
1650
- }
1651
- let updatedCssClassValue: string = this.cssClass;
1652
- if (!isNullOrUndefined(this.cssClass) && this.cssClass !== '') {
1653
- updatedCssClassValue = (this.cssClass.replace(/\s+/g, ' ')).trim();
1654
- }
1655
- if (!isNullOrUndefined(this.cssClass) && updatedCssClassValue !== '') {
1656
- addClass([this.uploadWrapper], updatedCssClassValue.split(updatedCssClassValue.indexOf(',') > -1 ? ',' : ' '));
1657
- }
1658
- }
1659
- private wireEvents(): void {
1660
- EventHandler.add(this.browseButton, 'click', this.browseButtonClick, this);
1661
- EventHandler.add(this.element, 'change', this.onSelectFiles, this);
1662
- EventHandler.add(document, 'click', this.removeFocus, this);
1663
- this.keyboardModule = new KeyboardEvents( this.uploadWrapper, {
1664
- keyAction: this.keyActionHandler.bind(this),
1665
- keyConfigs: this.keyConfigs,
1666
- eventName: 'keydown'
1667
- });
1668
- if (this.isForm) {
1669
- EventHandler.add(this.formElement, 'reset', this.resetForm, this);
1670
- }
1671
- }
1672
-
1673
- private unWireEvents(): void {
1674
- EventHandler.remove(this.browseButton, 'click', this.browseButtonClick);
1675
- EventHandler.remove(this.element, 'change', this.onSelectFiles);
1676
- EventHandler.remove(document, 'click', this.removeFocus);
1677
- if (this.isForm) {
1678
- EventHandler.remove(this.formElement, 'reset', this.resetForm);
1679
- }
1680
- if (this.keyboardModule) {
1681
- this.keyboardModule.destroy();
1682
- }
1683
- }
1684
-
1685
- private resetForm() : void {
1686
- this.clearAll();
1687
- }
1688
-
1689
- private keyActionHandler(e: KeyboardEventArgs): void {
1690
- const targetElement: HTMLElement = e.target as HTMLElement;
1691
- switch (e.action) {
1692
- case 'enter':
1693
- if (e.target === this.clearButton) {
1694
- this.clearButtonClick();
1695
- } else if (e.target === this.uploadButton) {
1696
- this.uploadButtonClick();
1697
- } else if (e.target === this.browseButton) {
1698
- this.browseButtonClick();
1699
- } else if (targetElement.classList.contains(PAUSE_UPLOAD)) {
1700
- const metaData: MetaData = this.getCurrentMetaData(null, e);
1701
- metaData.file.statusCode = '4';
1702
- metaData.file.status = this.localizedTexts('pauseUpload');
1703
- this.abortUpload(metaData, false);
1704
- } else if (targetElement.classList.contains(RESUME_UPLOAD)) {
1705
- this.resumeUpload(this.getCurrentMetaData(null, e), e);
1706
- } else if (targetElement.classList.contains(RETRY_ICON)) {
1707
- const metaData: MetaData = this.getCurrentMetaData(null, e);
1708
- if (!isNullOrUndefined(metaData)) {
1709
- metaData.file.statusCode = '1';
1710
- metaData.file.status = this.localizedTexts('readyToUploadMessage');
1711
- this.chunkUpload(metaData.file);
1712
- } else {
1713
- const target: HTMLElement = (<HTMLElement>e.target).parentElement;
1714
- const fileData: FileInfo = this.filesData[this.fileList.indexOf(target)];
1715
- this.retry(fileData);
1716
- }
1717
- } else {
1718
- this.removeFiles(e);
1719
- if (!targetElement.classList.contains(ABORT_ICON)) {
1720
- this.browseButton.focus();
1721
- }
1722
- }
1723
- e.preventDefault();
1724
- e.stopPropagation();
1725
- break;
1726
- }
1727
- }
1728
-
1729
- private getCurrentMetaData(fileInfo?: FileInfo, e?: KeyboardEventArgs) : MetaData {
1730
- let fileData: FileInfo; let targetMetaData : MetaData;
1731
- if (isNullOrUndefined(fileInfo)) {
1732
- const target: HTMLElement = (<HTMLElement>e.target).parentElement;
1733
- fileData = this.filesData[this.fileList.indexOf(target)];
1734
- } else {
1735
- fileData = fileInfo;
1736
- }
1737
- for (let i: number = 0; i < this.uploadMetaData.length; i++) {
1738
- if (this.uploadMetaData[i as number].file.name === fileData.name) {
1739
- targetMetaData = this.uploadMetaData[i as number];
1740
- }
1741
- }
1742
- return targetMetaData;
1743
- }
1744
-
1745
- private removeFocus() : void {
1746
- if (this.uploadWrapper && this.listParent && this.listParent.querySelector('.' + ICON_FOCUSED)) {
1747
- (<HTMLElement>document.activeElement).blur();
1748
- this.listParent.querySelector('.' + ICON_FOCUSED).classList.remove(ICON_FOCUSED);
1749
- }
1750
- }
1751
-
1752
- private browseButtonClick(): void {
1753
- this.element.click();
1754
- }
1755
-
1756
- private uploadButtonClick(): void {
1757
- if (this.sequentialUpload) {
1758
- this.sequenceUpload(this.filesData);
1759
- } else {
1760
- this.upload(this.filesData);
1761
- }
1762
- }
1763
-
1764
- private clearButtonClick(): void {
1765
- this.clearAll();
1766
- /* istanbul ignore next */
1767
- if (this.sequentialUpload) {
1768
- this.count = -1;
1769
- }
1770
- this.actionCompleteCount = 0;
1771
- }
1772
-
1773
- private bindDropEvents(): void {
1774
- if (this.dropZoneElement) {
1775
- EventHandler.add(this.dropZoneElement, 'drop', this.dropElement, this);
1776
- EventHandler.add(this.dropZoneElement, 'dragover', this.dragHover, this);
1777
- EventHandler.add(this.dropZoneElement, 'dragleave', this.onDragLeave, this);
1778
- EventHandler.add(this.dropZoneElement, 'paste', this.onPasteFile, this);
1779
- EventHandler.add(this.dropZoneElement, 'dragenter', this.onDragEnter, this);
1780
- }
1781
- }
1782
-
1783
- private unBindDropEvents(): void {
1784
- if (this.dropZoneElement) {
1785
- EventHandler.remove(this.dropZoneElement, 'drop', this.dropElement);
1786
- EventHandler.remove(this.dropZoneElement, 'dragover', this.dragHover);
1787
- EventHandler.remove(this.dropZoneElement, 'dragleave', this.onDragLeave);
1788
- EventHandler.remove(this.dropZoneElement, 'dragenter', this.onDragEnter);
1789
- }
1790
- }
1791
-
1792
- private onDragEnter( e : DragEvent) : void {
1793
- if (!this.enabled) {
1794
- return;
1795
- }
1796
- this.dropZoneElement.classList.add(DRAG_HOVER);
1797
- this.dragCounter = this.dragCounter + 1;
1798
- e.preventDefault();
1799
- e.stopPropagation();
1800
- }
1801
- private onDragLeave(): void {
1802
- if (!this.enabled) {
1803
- return;
1804
- }
1805
- this.dragCounter = this.dragCounter - 1;
1806
- if (!this.dragCounter) {
1807
- this.dropZoneElement.classList.remove(DRAG_HOVER);
1808
- }
1809
- }
1810
-
1811
- private dragHover(e: DragEvent): void {
1812
- if (!this.enabled) {
1813
- return;
1814
- }
1815
- if (this.dropEffect !== 'Default') {
1816
- e.dataTransfer.dropEffect = this.dropEffect.toLowerCase();
1817
- }
1818
- e.preventDefault();
1819
- e.stopPropagation();
1820
- }
1821
-
1822
- /* istanbul ignore next */
1823
- private dropElement(e: DragEvent): void {
1824
- this.dragCounter = 0;
1825
- this.dropZoneElement.classList.remove(DRAG_HOVER);
1826
- this.onSelectFiles(e);
1827
- e.preventDefault();
1828
- e.stopPropagation();
1829
- }
1830
-
1831
- /* istanbul ignore next */
1832
- private onPasteFile(event: ClipboardEvent): void {
1833
- const item: DataTransferItemList = event.clipboardData.items;
1834
- if (event.type === 'paste' && this.browserName !== 'msie' && this.browserName !== 'edge' && this.browserName !== 'safari') {
1835
- this.element.files = event.clipboardData.files;
1836
- }
1837
- if (item.length !== 1 && !this.multiple) {
1838
- return;
1839
- }
1840
- for (let file: number = 0; file < item.length; file++){
1841
- const pasteFile: DataTransferItem = [].slice.call(item)[file as number];
1842
- if (!isNullOrUndefined(pasteFile.getAsFile()) && ((pasteFile.kind === 'file') || pasteFile.type.match('^image/'))) {
1843
- this.renderSelectedFiles(event, [pasteFile.getAsFile()], false, true);
1844
- }
1845
- }
1846
- }
1847
-
1848
- private getSelectedFiles(index: number): FileInfo[] {
1849
- const data: FileInfo[] = [];
1850
- const liElement: HTMLElement = this.fileList[index as number];
1851
- const allFiles: FileInfo[] = this.getFilesData();
1852
- const nameElements: number = +liElement.getAttribute('data-files-count');
1853
- let startIndex: number = 0 ;
1854
- for (let i: number = 0; i < index; i++ ) {
1855
- startIndex += (+this.fileList[i as number].getAttribute('data-files-count'));
1856
- }
1857
- for (let j: number = startIndex; j < (startIndex + nameElements); j++) {
1858
- data.push(allFiles[j as number]);
1859
- }
1860
- return data;
1861
- }
1862
-
1863
- private removeFiles(args: MouseEvent | TouchEvent | KeyboardEventArgs): void {
1864
- if (!this.enabled) {
1865
- return;
1866
- }
1867
- const selectedElement: HTMLElement = (<HTMLInputElement>args.target).parentElement;
1868
- const index: number = this.fileList.indexOf(selectedElement);
1869
- const liElement: HTMLElement = this.fileList[index as number];
1870
- const formUpload: boolean = this.isFormUpload();
1871
- const fileData: FileInfo[] = formUpload ? this.getSelectedFiles(index) : this.getFilesInArray(this.filesData[index as number]);
1872
- if (isNullOrUndefined(fileData)) {
1873
- return;
1874
- }
1875
- if ((<HTMLInputElement>args.target).classList.contains(ABORT_ICON) && !formUpload) {
1876
- fileData[0].statusCode = '5';
1877
- if (!isNullOrUndefined(liElement)) {
1878
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + ABORT_ICON) as HTMLElement;
1879
- createSpinner({ target: spinnerTarget , width: '20px' });
1880
- showSpinner(spinnerTarget);
1881
- }
1882
- if (this.sequentialUpload) {
1883
- /* istanbul ignore next */
1884
- this.uploadSequential();
1885
- }
1886
- if (!(liElement.classList.contains(RESTRICT_RETRY))) {
1887
- this.checkActionComplete(true);
1888
- }
1889
- } else if (!closest(args.target as Element, '.' + SPINNER_PANE)) {
1890
- this.remove(fileData, false, false, true, args);
1891
- }
1892
- if (this.isForm && liElement && liElement.classList.contains(INVALID_FILE)){
1893
- this.element.value = '';
1894
- }
1895
- this.checkActionButtonStatus();
1896
- if (this.actionButtons && this.clearButton && this.uploadWrapper) {
1897
- const progressBarElements: NodeListOf<Element> = this.uploadWrapper.querySelectorAll(
1898
- '.e-upload-progress-bar.e-upload-progress:not(.e-upload-success):not(.e-upload-fails)');
1899
- if (progressBarElements.length === 1 && selectedElement.contains(progressBarElements[0])
1900
- && this.clearButton.hasAttribute('disabled')) {
1901
- this.clearButton.removeAttribute('disabled');
1902
- }
1903
- }
1904
- }
1905
-
1906
- private removeFilesData(file: FileInfo, customTemplate: boolean): void {
1907
- let index: number ;
1908
- if (customTemplate) {
1909
- if (!this.showFileList) {
1910
- index = this.filesData.indexOf(file);
1911
- this.filesData.splice(index, 1);
1912
- }
1913
- return;
1914
- }
1915
- const selectedElement: HTMLElement = this.getLiElement(file);
1916
- if (isNullOrUndefined(selectedElement)) {
1917
- return;
1918
- }
1919
- this.element.value = '';
1920
- detach(selectedElement);
1921
- index = this.fileList.indexOf(selectedElement);
1922
- this.fileList.splice(index, 1);
1923
- this.filesData.splice(index, 1);
1924
- if (this.fileList.length === 0 && !isNullOrUndefined(this.listParent)) {
1925
- detach(this.listParent);
1926
- this.listParent = null;
1927
- this.removeActionButtons();
1928
- }
1929
- if (this.sequentialUpload) {
1930
- /* istanbul ignore next */
1931
- if (index <= this.count) {
1932
- --this.count;
1933
- }
1934
- }
1935
- }
1936
-
1937
- private removeUploadedFile(
1938
- file: FileInfo, eventArgs: RemovingEventArgs,
1939
- removeDirectly: boolean, custom: boolean): void {
1940
- const selectedFiles: FileInfo = file;
1941
- const ajax: Ajax = new Ajax(this.asyncSettings.removeUrl, 'POST', true, null);
1942
- ajax.emitError = false;
1943
- const formData: FormData = new FormData();
1944
- ajax.beforeSend = (e: BeforeSendEventArgs) => {
1945
- eventArgs.currentRequest = ajax.httpRequest;
1946
- if (!removeDirectly) {
1947
- this.trigger('removing', eventArgs, (eventArgs: RemovingEventArgs) => {
1948
- if (eventArgs.cancel) {
1949
- e.cancel = true;
1950
- } else {
1951
- this.removingEventCallback(eventArgs, formData, selectedFiles, file);
1952
- }
1953
- });
1954
- } else {
1955
- this.removingEventCallback(eventArgs, formData, selectedFiles, file);
1956
- }
1957
- };
1958
- ajax.onLoad = (e: Event): object => {
1959
- this.removeCompleted(e, selectedFiles, custom); return {};
1960
- };
1961
- /* istanbul ignore next */
1962
- ajax.onError = (e: Event): object => {
1963
- this.removeFailed(e, selectedFiles, custom); return {};
1964
- };
1965
- ajax.send(formData);
1966
- }
1967
-
1968
- private removingEventCallback(eventArgs: RemovingEventArgs, formData: FormData, selectedFiles: FileInfo, file: FileInfo): void {
1969
- /* istanbul ignore next */
1970
- const name: string = this.element.getAttribute('name');
1971
- const liElement: HTMLElement = this.getLiElement(file);
1972
- if (!isNullOrUndefined(liElement) && (!isNullOrUndefined(liElement.querySelector('.' + DELETE_ICON)) ||
1973
- !isNullOrUndefined(liElement.querySelector('.' + REMOVE_ICON)))) {
1974
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + DELETE_ICON) ?
1975
- liElement.querySelector('.' + DELETE_ICON) as HTMLElement :
1976
- liElement.querySelector('.' + REMOVE_ICON) as HTMLElement;
1977
- createSpinner({ target: spinnerTarget , width: '20px' });
1978
- showSpinner(spinnerTarget);
1979
- }
1980
- if (eventArgs.postRawFile && !isNullOrUndefined(selectedFiles.rawFile) && selectedFiles.rawFile !== '') {
1981
- formData.append(name, selectedFiles.rawFile, selectedFiles.name);
1982
- } else {
1983
- formData.append(name, selectedFiles.name);
1984
- }
1985
- this.updateFormData(formData, eventArgs.customFormData);
1986
- }
1987
-
1988
- /* istanbul ignore next */
1989
- private updateFormData(formData: FormData, customData: { [key: string]: Object }[]): void {
1990
- if (customData.length > 0 && customData[0]) {
1991
- for (let i: number = 0; i < customData.length; i++) {
1992
- const data: { [key: string]: Object } = customData[i as number];
1993
- // eslint-disable-next-line @typescript-eslint/tslint/config
1994
- const value: any = Object.keys(data).map(function(e) {
1995
- return data[`${e}`];
1996
- });
1997
- formData.append(Object.keys(data)[0], value);
1998
- }
1999
- }
2000
- }
2001
-
2002
- /* istanbul ignore next */
2003
- private updateCustomheader(request: XMLHttpRequest, currentRequest: { [key: string]: string }[]): void {
2004
- if (currentRequest.length > 0 && currentRequest[0]) {
2005
- for (let i: number = 0; i < currentRequest.length; i++) {
2006
- const data: { [key: string]: string } = currentRequest[i as number];
2007
- // eslint-disable-next-line @typescript-eslint/tslint/config
2008
- const value: any = Object.keys(data).map(function (e) {
2009
- return data[`${e}`];
2010
- });
2011
- request.setRequestHeader(Object.keys(data)[0], value);
2012
- }
2013
- }
2014
- }
2015
-
2016
- private removeCompleted(e: Event, files: FileInfo, customTemplate: boolean): void {
2017
- const response: Object = e && e.currentTarget ? this.getResponse(e) : null;
2018
- const status : XMLHttpRequest = e.target as XMLHttpRequest;
2019
- if (status.readyState === 4 && status.status >= 200 && status.status <= 299 ) {
2020
- const args : Object = {
2021
- e, response: response, operation: 'remove', file: this.updateStatus(
2022
- files, this.localizedTexts('removedSuccessMessage'), '2')
2023
- };
2024
- this.trigger('success', args);
2025
- this.removeFilesData(files, customTemplate);
2026
- const index: number = this.uploadedFilesData.indexOf(files);
2027
- this.uploadedFilesData.splice(index, 1);
2028
- this.trigger('change', { files: this.uploadedFilesData });
2029
- } else {
2030
- this.removeFailed(e, files, customTemplate);
2031
- }
2032
- }
2033
-
2034
- private removeFailed(e: Event, files: FileInfo, customTemplate: boolean): void {
2035
- const response: Object = e && e.currentTarget ? this.getResponse(e) : null;
2036
- const args : Object = {
2037
- e, response: response, operation: 'remove', file: this.updateStatus(files, this.localizedTexts('removedFailedMessage'), '0')
2038
- };
2039
- if (!customTemplate) {
2040
- const index: number = this.filesData.indexOf(files);
2041
- const rootElement: HTMLElement = this.fileList[index as number];
2042
- if (rootElement) {
2043
- rootElement.classList.remove(UPLOAD_SUCCESS);
2044
- rootElement.classList.add(UPLOAD_FAILED);
2045
- const statusElement: HTMLElement = rootElement.querySelector('.' + STATUS) as HTMLElement;
2046
- if (statusElement) {
2047
- statusElement.classList.remove(UPLOAD_SUCCESS);
2048
- statusElement.classList.add(UPLOAD_FAILED);
2049
- }
2050
- }
2051
- this.checkActionButtonStatus();
2052
- }
2053
- this.trigger('failure', args);
2054
- const liElement: HTMLElement = this.getLiElement(files);
2055
- /* istanbul ignore next */
2056
- if (!isNullOrUndefined(liElement) && !isNullOrUndefined(liElement.querySelector('.' + DELETE_ICON))) {
2057
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + DELETE_ICON) as HTMLElement;
2058
- hideSpinner(spinnerTarget);
2059
- detach(liElement.querySelector('.e-spinner-pane'));
2060
- }
2061
- }
2062
-
2063
- /* istanbul ignore next */
2064
- private getFilesFromFolder(event: MouseEvent | TouchEvent | DragEvent | ClipboardEvent): void {
2065
- this.filesEntries = [];
2066
- const items: DataTransferItem[] | DataTransferItemList = this.multiple ?
2067
- (<DragEvent>event).dataTransfer.items : [(<DragEvent>event).dataTransfer.items[0]];
2068
- const validDirectoryUpload: boolean = this.checkDirectoryUpload(items);
2069
- if (!validDirectoryUpload) {
2070
- return;
2071
- }
2072
- for (let i: number = 0; i < items.length; i++) {
2073
- const item: any = items[i as number].webkitGetAsEntry();
2074
- if (item.isFile) {
2075
- const files: { [key: string]: Object }[] = [];
2076
- (item).file( (fileObj: any) => {
2077
- const path: string = item.fullPath;
2078
- files.push({'path': path, 'file': fileObj});
2079
- });
2080
- this.renderSelectedFiles(event, files, true);
2081
- } else if (item.isDirectory) {
2082
- this.traverseFileTree(item, event);
2083
- }
2084
- }
2085
- }
2086
-
2087
- /* istanbul ignore next */
2088
- private checkDirectoryUpload(items: DataTransferItem[] | DataTransferItemList): boolean {
2089
- for (let i: number = 0; items && i < items.length; i++) {
2090
- const item : any = items[i as number].webkitGetAsEntry();
2091
- if (item.isDirectory) {
2092
- return true;
2093
- }
2094
- }
2095
- return false;
2096
- }
2097
- /* istanbul ignore next */
2098
- public traverseFileTree(item: any, event?: MouseEvent | TouchEvent | DragEvent | ClipboardEvent): void {
2099
- if (item.isFile) {
2100
- this.filesEntries.push(item);
2101
- } else if (item.isDirectory) {
2102
- const directoryReader: any = item.createReader();
2103
- this.readFileFromDirectory(directoryReader, event);
2104
- }
2105
- }
2106
-
2107
- /* istanbul ignore next */
2108
- private readFileFromDirectory(directoryReader: any, event?: MouseEvent | TouchEvent | DragEvent | ClipboardEvent): void {
2109
- directoryReader.readEntries( (entries: any) => {
2110
- for (let i: number = 0; i < entries.length; i++) {
2111
- this.traverseFileTree(entries[i as number], event);
2112
- }
2113
- this.pushFilesEntries(event);
2114
- if (entries.length) {
2115
- this.readFileFromDirectory(directoryReader);
2116
- }
2117
- });
2118
- }
2119
-
2120
- private pushFilesEntries(event?: MouseEvent | TouchEvent | DragEvent | ClipboardEvent): void {
2121
- const files: { [key: string]: Object }[] = [];
2122
- for (let i: number = 0; i < this.filesEntries.length; i++) {
2123
- this.filesEntries[i as number].file( (fileObj: any) => {
2124
- if (this.filesEntries.length) {
2125
- const path: string = this.filesEntries[i as number].fullPath;
2126
- files.push({'path': path, 'file': fileObj});
2127
- if (i === this.filesEntries.length - 1) {
2128
- this.filesEntries = [];
2129
- this.renderSelectedFiles(event, files, true);
2130
- }
2131
- }
2132
- });
2133
- }
2134
- }
2135
-
2136
- private onSelectFiles(args: MouseEvent | TouchEvent | DragEvent | ClipboardEvent): void {
2137
- if (!this.enabled) {
2138
- return;
2139
- }
2140
- let targetFiles: File[];
2141
- /* istanbul ignore next */
2142
- if (args.type === 'drop') {
2143
- if (this.directoryUpload) {
2144
- this.getFilesFromFolder(args);
2145
- } else {
2146
- const files: FileList = this.sortFilesList = (<DragEvent>args).dataTransfer.files;
2147
- if (this.browserName !== 'msie' && this.browserName !== 'edge' && this.browserName !== 'safari') {
2148
- this.element.files = files;
2149
- }
2150
- if (files.length > 0) {
2151
- targetFiles = this.multiple ? this.sortFileList(files) : [files[0]];
2152
- this.renderSelectedFiles(args, targetFiles);
2153
- }
2154
- }
2155
- } else {
2156
- targetFiles = [].slice.call((<HTMLInputElement>args.target).files);
2157
- this.renderSelectedFiles(args, targetFiles);
2158
- }
2159
- if (this.isAngular || (this as any).isReact) {
2160
- args.stopPropagation();
2161
- }
2162
-
2163
- }
2164
- /* istanbul ignore next */
2165
- private getBase64(file: File): Promise<void> {
2166
- return new Promise((resolve: Function, reject: Function) => {
2167
- const fileReader: FileReader = new FileReader();
2168
- fileReader.readAsDataURL(file);
2169
- fileReader.onload = () => resolve(fileReader.result as string);
2170
- fileReader.onerror = (error: ProgressEvent) => reject(error);
2171
- });
2172
- }
2173
-
2174
- /* istanbul ignore next */
2175
- /* tslint:ignore */
2176
- private renderSelectedFiles(
2177
- args: MouseEvent | TouchEvent | DragEvent | ClipboardEvent,
2178
- targetFiles: any, directory?: boolean, paste?: boolean): void {
2179
- this.base64String = [];
2180
- const eventArgs: SelectedEventArgs = {
2181
- event: args,
2182
- cancel: false,
2183
- filesData: [],
2184
- isModified: false,
2185
- modifiedFilesData: [],
2186
- progressInterval: '',
2187
- isCanceled: false,
2188
- currentRequest: null,
2189
- customFormData: null
2190
- };
2191
- /* istanbul ignore next */
2192
- if (targetFiles.length < 1) {
2193
- eventArgs.isCanceled = true;
2194
- this.trigger('selected', eventArgs);
2195
- return;
2196
- }
2197
- this.flag = true;
2198
- let fileData: FileInfo[] = [];
2199
- if (!this.multiple) {
2200
- this.clearData(true);
2201
- this.actionCompleteCount = 0;
2202
- targetFiles = [targetFiles[0]];
2203
- }
2204
- for (let i: number = 0; i < targetFiles.length; i++) {
2205
- const file: File = directory ? targetFiles[i as number].file : targetFiles[i as number];
2206
- this.updateInitialFileDetails(args, targetFiles, file, i, fileData, directory, paste);
2207
- }
2208
- eventArgs.filesData = fileData;
2209
- if (!isNullOrUndefined(this.allowedExtensions) && this.allowedExtensions.indexOf('*') > -1) {
2210
- this.allTypes = true;
2211
- }
2212
- if (this.enableHtmlSanitizer){
2213
- for (let i: number = 0; i < fileData.length; i++) {
2214
- const sanitizeFile: any = SanitizeHtmlHelper.beforeSanitize();
2215
- const sanitizeFileName: string = SanitizeHtmlHelper.serializeValue(sanitizeFile, fileData[parseInt(i.toString(), 10)].name);
2216
- const currentFileName: string = fileData[parseInt(i.toString(), 10)].name;
2217
- let isUTF8: boolean = false;
2218
- for (let i: number = 0; i < currentFileName.length; i++) {
2219
- if (currentFileName.charCodeAt(i) > 127) {
2220
- isUTF8 = true;
2221
- break;
2222
- }
2223
- }
2224
- const htmlTagRegex: RegExp = /<([a-z][a-z0-9]*)\b[^>]*>(.*?)<\/\1>/i;
2225
- const hasHTMLString: boolean = htmlTagRegex.test(currentFileName);
2226
- if ((sanitizeFileName !== fileData[parseInt(i.toString(), 10)].name) && !(isUTF8 && !hasHTMLString)) {
2227
- const encodedFileName: any = targetFiles[parseInt(i.toString(), 10)].name.replace(/[\u00A0-\u9999<>\\&]/g, function (i: any): string {
2228
- return '&#' + i.charCodeAt(0) + ';';
2229
- });
2230
- fileData[parseInt(i.toString(), 10)].name = encodedFileName;
2231
- fileData[parseInt(i.toString(), 10)].status = this.localizedTexts('invalidFileName');
2232
- fileData[parseInt(i.toString(), 10)].statusCode = '0';
2233
- }
2234
- }
2235
- }
2236
- if (!this.allTypes) {
2237
- fileData = this.checkExtension(fileData);
2238
- }
2239
- this.trigger('selected', eventArgs, (eventArgs: SelectedEventArgs) => {
2240
- this._internalRenderSelect(eventArgs, fileData);
2241
- });
2242
- }
2243
-
2244
- private updateInitialFileDetails(args: MouseEvent | TouchEvent | DragEvent | ClipboardEvent,
2245
- targetFiles: any, file: File, i: number, fileData: FileInfo[],
2246
- directory?: boolean, paste?: boolean): void {
2247
- const fileName: string = directory ? targetFiles[i as number].path.substring(1, targetFiles[i as number].path.length) : paste ?
2248
- getUniqueID(file.name.substring(0, file.name.lastIndexOf('.'))) + '.' + this.getFileType(file.name) :
2249
- this.directoryUpload ? targetFiles[i as number].webkitRelativePath : file.name;
2250
- const fileDetails: FileInfo = {
2251
- name: fileName,
2252
- rawFile: file,
2253
- size: file.size,
2254
- status: this.localizedTexts('readyToUploadMessage'),
2255
- type: this.getFileType(file.name),
2256
- validationMessages: this.validatedFileSize(file.size),
2257
- statusCode: '1',
2258
- id: getUniqueID(file.name.substring(0, file.name.lastIndexOf('.'))) + '.' + this.getFileType(file.name)
2259
- };
2260
- /* istanbul ignore next */
2261
- if (paste) {
2262
- fileDetails.fileSource = 'paste';
2263
- }
2264
- fileDetails.status = fileDetails.validationMessages.minSize !== '' ? this.localizedTexts('invalidMinFileSize') :
2265
- fileDetails.validationMessages.maxSize !== '' ? this.localizedTexts('invalidMaxFileSize') : fileDetails.status;
2266
- if (fileDetails.validationMessages.minSize !== '' || fileDetails.validationMessages.maxSize !== '') {
2267
- fileDetails.statusCode = '0';
2268
- this.checkActionComplete(true);
2269
- }
2270
- fileData.push(fileDetails);
2271
- }
2272
-
2273
- private _internalRenderSelect(eventArgs: SelectedEventArgs, fileData: FileInfo[]): void {
2274
- if (!eventArgs.cancel) {
2275
- /* istanbul ignore next */
2276
- this.selectedFiles = this.selectedFiles.concat(fileData);
2277
- this.btnTabIndex = this.disableKeyboardNavigation ? '-1' : '0';
2278
- if (this.showFileList) {
2279
- if (eventArgs.isModified && eventArgs.modifiedFilesData.length > 0) {
2280
- for (let j: number = 0; j < eventArgs.modifiedFilesData.length; j++) {
2281
- for (let k: number = 0; k < fileData.length; k++) {
2282
- if (eventArgs.modifiedFilesData[j as number].id === fileData[k as number].id) {
2283
- eventArgs.modifiedFilesData[j as number].rawFile = fileData[k as number].rawFile;
2284
- }
2285
- }
2286
- }
2287
- const dataFiles: FileInfo[] = this.allTypes ? eventArgs.modifiedFilesData :
2288
- this.checkExtension(eventArgs.modifiedFilesData);
2289
- this.updateSortedFileList(dataFiles);
2290
- this.filesData = this.filesData.concat(dataFiles);
2291
- if (!this.isForm || this.allowUpload()) {
2292
- this.checkAutoUpload(dataFiles);
2293
- }
2294
- } else {
2295
- this.internalCreateFileList(fileData);
2296
- if (this.autoUpload && this.sequenceUpload && this.sequentialUpload && this.filesData.length > 0 && this.filesData[this.filesData.length - 1 as number].statusCode !== '2' && this.filesData[this.filesData.length - 1 as number].statusCode !== '0') {
2297
- this.filesData = this.filesData.concat(fileData);
2298
- return;
2299
- }
2300
- this.filesData = this.filesData.concat(fileData);
2301
- if (!this.isForm || this.allowUpload()) {
2302
- this.checkAutoUpload(fileData);
2303
- }
2304
- }
2305
- if (!isNullOrUndefined(eventArgs.progressInterval) && eventArgs.progressInterval !== '') {
2306
- this.progressInterval = eventArgs.progressInterval;
2307
- }
2308
- } else {
2309
- this.filesData = this.filesData.concat(fileData);
2310
- if (this.autoUpload) {
2311
- this.upload(this.filesData, true);
2312
- }
2313
- }
2314
- this.raiseActionComplete();
2315
- this.isFirstFileOnSelection = true;
2316
- }
2317
- }
2318
-
2319
- private allowUpload(): boolean {
2320
- let allowFormUpload: boolean = false;
2321
- if (this.isForm && (!isNullOrUndefined(this.asyncSettings.saveUrl) && this.asyncSettings.saveUrl !== '' )) {
2322
- allowFormUpload = true;
2323
- }
2324
- return allowFormUpload;
2325
- }
2326
-
2327
- private isFormUpload(): boolean {
2328
- let isFormUpload: boolean = false;
2329
- if (this.isForm && ((isNullOrUndefined(this.asyncSettings.saveUrl) || this.asyncSettings.saveUrl === '' )
2330
- && (isNullOrUndefined(this.asyncSettings.removeUrl) || this.asyncSettings.removeUrl === '' ))) {
2331
- isFormUpload = true;
2332
- }
2333
- return isFormUpload;
2334
- }
2335
-
2336
- private clearData(singleUpload?: boolean) : void {
2337
- if (!isNullOrUndefined(this.listParent)) {
2338
- detach(this.listParent);
2339
- this.listParent = null;
2340
- }
2341
- if (this.browserName !== 'msie' && !singleUpload) {
2342
- this.element.value = '';
2343
- }
2344
- this.fileList = [];
2345
- this.filesData = [];
2346
- this.removeActionButtons();
2347
- }
2348
-
2349
- private updateSortedFileList(filesData: FileInfo[]): void {
2350
- const previousListClone: HTMLElement = this.createElement('div', {id: 'clonewrapper'});
2351
- let added: number = -1;
2352
- if (this.listParent) {
2353
- for (let i: number = 0; i < this.listParent.querySelectorAll('li').length; i++) {
2354
- const liElement: HTMLElement = this.listParent.querySelectorAll('li')[i as number];
2355
- previousListClone.appendChild(liElement.cloneNode(true));
2356
- }
2357
- this.removeActionButtons();
2358
- const oldList: HTMLElement[] = [].slice.call(previousListClone.childNodes);
2359
- this.createParentUL();
2360
- for (let index: number = 0; index < filesData.length; index++) {
2361
- for (let j: number = 0; j < this.filesData.length; j++) {
2362
- if (this.filesData[j as number].name === filesData[index as number].name) {
2363
- this.listParent.appendChild(oldList[j as number]);
2364
- EventHandler.add(oldList[j as number].querySelector('.e-icons'), 'click', this.removeFiles, this);
2365
- this.fileList.push(oldList[j as number]);
2366
- added = index;
2367
- }
2368
- }
2369
- if (added !== index) {
2370
- this.internalCreateFileList([filesData[index as number]]);
2371
- }
2372
- }
2373
- } else {
2374
- this.internalCreateFileList(filesData);
2375
- }
2376
- }
2377
-
2378
- private isBlank(str: string): boolean {
2379
- return (!str || /^\s*$/.test(str));
2380
- }
2381
-
2382
- private checkGenericExtension(genericType: string, fileExtension: string): boolean {
2383
- const validExtensions: string[] = [];
2384
-
2385
- switch (genericType.toLowerCase()) {
2386
- case 'image/*':
2387
- validExtensions.push('jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'svg', 'webp', 'heic');
2388
- break;
2389
- case 'audio/*':
2390
- validExtensions.push('mp3', 'wav', 'aac', 'flac', 'ogg', 'm4a');
2391
- break;
2392
- case 'video/*':
2393
- validExtensions.push('mp4', 'mov', 'avi', 'mkv', 'flv', 'wmv', 'webm', 'mpeg');
2394
- break;
2395
- case 'application/*':
2396
- validExtensions.push('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', '7z', 'tar', 'pdf');
2397
- break;
2398
- case 'text/*':
2399
- validExtensions.push('txt', 'csv', 'html', 'css', 'js', 'json', 'xml', 'md');
2400
- break;
2401
- default:
2402
- break;
2403
- }
2404
-
2405
- return (validExtensions as any).includes(fileExtension.toLowerCase());
2406
- }
2407
-
2408
- private checkExtension(files: FileInfo[]): FileInfo[] {
2409
- const dropFiles: FileInfo[] = files;
2410
- if (!this.isBlank(this.allowedExtensions)) {
2411
- const allowedExtensions: string[] = [];
2412
- const extensions: string[] = !isNullOrUndefined(allowedExtensions) ? this.allowedExtensions.split(',') : [''];
2413
- for (const extension of extensions) {
2414
- allowedExtensions.push(extension.trim().toLocaleLowerCase());
2415
- }
2416
- for (let i: number = 0; i < files.length; i++) {
2417
- const checkFileType: string = files[i as number].type.indexOf('.') !== -1 ?
2418
- files[i as number].type.replace('.', '') : files[i as number].type;
2419
- if (allowedExtensions[0].indexOf('/*') !== -1) {
2420
- const isValidExtension : boolean = this.checkGenericExtension(allowedExtensions[0], checkFileType);
2421
- if (!isValidExtension)
2422
- {
2423
- files[i as number].status = this.localizedTexts('invalidFileType');
2424
- files[i as number].statusCode = '0';
2425
- }
2426
- }
2427
- else if ((allowedExtensions.indexOf(('.' + checkFileType).toLocaleLowerCase()) === -1)) {
2428
- files[i as number].status = this.localizedTexts('invalidFileType');
2429
- files[i as number].statusCode = '0';
2430
- }
2431
- }
2432
- }
2433
- return dropFiles;
2434
- }
2435
-
2436
- private validatedFileSize(fileSize: number): Object {
2437
- let minSizeError: string = '';
2438
- let maxSizeError: string = '';
2439
- if (fileSize < this.minFileSize) {
2440
- minSizeError = this.localizedTexts('invalidMinFileSize');
2441
- } else if (fileSize > this.maxFileSize) {
2442
- maxSizeError = this.localizedTexts('invalidMaxFileSize');
2443
- } else {
2444
- minSizeError = '';
2445
- maxSizeError = '';
2446
- }
2447
- const errorMessage: Object = { minSize: minSizeError, maxSize: maxSizeError };
2448
- return errorMessage;
2449
- }
2450
-
2451
- private isPreLoadFile(fileData: FileInfo) : boolean {
2452
- let isPreload: boolean = false;
2453
- for (let i : number = 0; i < this.files.length; i++) {
2454
- if (this.files[i as number].name === fileData.name.slice(0, fileData.name.lastIndexOf('.')) &&
2455
- this.files[i as number].type === fileData.type) {
2456
- isPreload = true;
2457
- }
2458
- }
2459
- return isPreload;
2460
- }
2461
-
2462
- private createCustomfileList(fileData: FileInfo[]): void {
2463
- this.createParentUL();
2464
- for (const listItem of fileData) {
2465
- const listElement: HTMLElement = this.createElement('li', { className: FILE, attrs: { 'data-file-name': listItem.name } });
2466
- this.uploadTemplateFn = this.templateComplier(this.template);
2467
- const liTempCompiler: any = this.uploadTemplateFn(
2468
- listItem, this, 'template', this.element.id + 'Template', this.isStringTemplate, null, listElement);
2469
- if (liTempCompiler) {
2470
- const fromElements: HTMLElement[] = [].slice.call(liTempCompiler);
2471
- append(fromElements, listElement);
2472
- }
2473
- const index: number = fileData.indexOf(listItem);
2474
- const eventArgs: RenderingEventArgs = {
2475
- element: listElement,
2476
- fileInfo: listItem,
2477
- index: index,
2478
- isPreload: this.isPreLoadFile(listItem)
2479
- };
2480
- const eventsArgs: FileListRenderingEventArgs = {
2481
- element: listElement,
2482
- fileInfo: listItem,
2483
- index: index,
2484
- isPreload: this.isPreLoadFile(listItem)
2485
- };
2486
- this.trigger('rendering', eventArgs);
2487
- this.trigger('fileListRendering', eventsArgs);
2488
- this.listParent.appendChild(listElement);
2489
- this.fileList.push(listElement);
2490
- }
2491
- this.renderReactTemplates();
2492
- }
2493
-
2494
- private createParentUL() : void {
2495
- if (isNullOrUndefined(this.listParent)) {
2496
- this.listParent = this.createElement('ul', { className: LIST_PARENT });
2497
- this.uploadWrapper.appendChild(this.listParent);
2498
- }
2499
- }
2500
-
2501
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2502
- private formFileList(fileData: FileInfo[], files: FileList): void {
2503
- const fileList: HTMLElement = this.createElement('li', { className: FILE });
2504
- fileList.setAttribute('data-files-count', fileData.length + '');
2505
- const fileContainer: HTMLElement = this.createElement('span', { className: TEXT_CONTAINER });
2506
- let statusMessage: string;
2507
- for (const listItem of fileData) {
2508
- const fileNameEle: HTMLElement = this.createElement('span', { className: FILE_NAME });
2509
- fileNameEle.innerHTML = this.getFileNameOnly(listItem.name);
2510
- const fileTypeEle: HTMLElement = this.createElement('span', { className: FILE_TYPE });
2511
- const fileType: string = this.getFileType(listItem.name);
2512
- fileTypeEle.innerHTML = '.' + fileType;
2513
- if (!fileType) {
2514
- fileTypeEle.classList.add('e-hidden');
2515
- }
2516
- if (!this.enableRtl) {
2517
- fileContainer.appendChild(fileNameEle);
2518
- fileContainer.appendChild(fileTypeEle);
2519
- } else {
2520
- const rtlContainer: Element = this.createElement('span', { className: RTL_CONTAINER });
2521
- rtlContainer.appendChild(fileTypeEle);
2522
- rtlContainer.appendChild(fileNameEle);
2523
- fileContainer.appendChild(rtlContainer);
2524
- }
2525
- this.truncateName(fileNameEle);
2526
- statusMessage = this.formValidateFileInfo(listItem, fileList);
2527
- }
2528
- fileList.appendChild(fileContainer);
2529
- this.setListToFileInfo(fileData, fileList);
2530
- const index: number = this.listParent.querySelectorAll('li').length;
2531
- const infoEle: HTMLElement = this.createElement('span');
2532
- if (fileList.classList.contains(INVALID_FILE)) {
2533
- infoEle.classList.add(STATUS);
2534
- infoEle.classList.add(INVALID_FILE);
2535
- infoEle.innerText = fileData.length > 1 ? this.localizedTexts('invalidFileSelection') : statusMessage;
2536
- } else {
2537
- infoEle.classList.add( fileData.length > 1 ? INFORMATION : FILE_SIZE);
2538
- infoEle.innerText = fileData.length > 1 ? this.localizedTexts('totalFiles') + ': ' + fileData.length + ' , '
2539
- + this.localizedTexts('size') + ': ' +
2540
- this.bytesToSize(this.getFileSize(fileData)) : this.bytesToSize(fileData[0].size);
2541
- this.createFormInput(fileData);
2542
- }
2543
- fileContainer.appendChild(infoEle);
2544
-
2545
- if (isNullOrUndefined(fileList.querySelector('.e-icons'))) {
2546
- const iconElement: HTMLElement = this.createElement('span', {className: 'e-icons', attrs: { 'tabindex': this.btnTabIndex}});
2547
- /* istanbul ignore next */
2548
- if (this.browserName === 'msie') {
2549
- iconElement.classList.add('e-msie');
2550
- }
2551
- iconElement.setAttribute('title', this.localizedTexts('remove'));
2552
- fileList.appendChild(fileContainer);
2553
- fileList.appendChild(iconElement);
2554
- EventHandler.add(iconElement, 'click', this.removeFiles, this);
2555
- iconElement.classList.add(REMOVE_ICON);
2556
- }
2557
-
2558
- const eventArgs: RenderingEventArgs = {
2559
- element: fileList,
2560
- fileInfo: this.mergeFileInfo(fileData, fileList),
2561
- index: index,
2562
- isPreload: this.isPreLoadFile(this.mergeFileInfo(fileData, fileList))
2563
- };
2564
- const eventsArgs: FileListRenderingEventArgs = {
2565
- element: fileList,
2566
- fileInfo: this.mergeFileInfo(fileData, fileList),
2567
- index: index,
2568
- isPreload: this.isPreLoadFile(this.mergeFileInfo(fileData, fileList))
2569
- };
2570
- this.trigger('rendering', eventArgs);
2571
- this.trigger('fileListRendering', eventsArgs);
2572
- this.listParent.appendChild(fileList);
2573
- this.fileList.push(fileList);
2574
- }
2575
-
2576
- private formValidateFileInfo(listItem: FileInfo, fileList: HTMLElement): string {
2577
- let statusMessage: string = listItem.status;
2578
- const validationMessages: ValidationMessages = this.validatedFileSize(listItem.size);
2579
- if (validationMessages.minSize !== '' || validationMessages.maxSize !== '') {
2580
- this.addInvalidClass(fileList);
2581
- statusMessage = validationMessages.minSize !== '' ? this.localizedTexts('invalidMinFileSize') :
2582
- validationMessages.maxSize !== '' ? this.localizedTexts('invalidMaxFileSize') : statusMessage;
2583
- }
2584
- const typeValidationMessage: string = this.checkExtension(this.getFilesInArray(listItem))[0].status;
2585
- if ( typeValidationMessage === this.localizedTexts('invalidFileType')) {
2586
- this.addInvalidClass(fileList);
2587
- statusMessage = typeValidationMessage;
2588
- }
2589
- return statusMessage;
2590
- }
2591
-
2592
- private addInvalidClass(fileList: HTMLElement): void {
2593
- fileList.classList.add(INVALID_FILE);
2594
- }
2595
-
2596
- private createFormInput(fileData: FileInfo[]): void {
2597
- if (this.browserName !== 'safari') {
2598
- const inputElement: HTMLInputElement = this.element.cloneNode(true) as HTMLInputElement;
2599
- inputElement.classList.add(HIDDEN_INPUT);
2600
- for (const listItem of fileData) {
2601
- listItem.input = inputElement;
2602
- }
2603
- inputElement.setAttribute('id', getUniqueID('hiddenUploader'));
2604
- inputElement.setAttribute('name', this.uploaderName);
2605
- this.uploadWrapper.querySelector('.' + INPUT_WRAPPER).appendChild(inputElement);
2606
- if (this.browserName !== 'msie' && this.browserName !== 'edge') {
2607
- this.element.value = ''; }
2608
- }
2609
- }
2610
-
2611
- private getFileSize(fileData: FileInfo[]): number {
2612
- let fileSize: number = 0;
2613
- for (const file of fileData) {
2614
- fileSize += file.size;
2615
- }
2616
- return fileSize;
2617
- }
2618
-
2619
- private mergeFileInfo(fileData: FileInfo[], fileList: HTMLElement): FileInfo {
2620
- const result: FileInfo = {
2621
- name: '',
2622
- rawFile: '',
2623
- size: 0,
2624
- status: '',
2625
- type: '',
2626
- validationMessages: { minSize: '', maxSize: ''},
2627
- statusCode: '1',
2628
- list: fileList
2629
- };
2630
- const fileNames: string [] = [];
2631
- let type: string = '';
2632
- for (const listItem of fileData) {
2633
- fileNames.push(listItem.name);
2634
- type = listItem.type;
2635
- }
2636
- result.name = fileNames.join(', ');
2637
- result.size = this.getFileSize(fileData);
2638
- result.type = type;
2639
- result.status = this.statusForFormUpload(fileData, fileList);
2640
- return result;
2641
- }
2642
-
2643
- private statusForFormUpload(fileData: FileInfo[], fileList: HTMLElement): string {
2644
- let isValid: boolean = true;
2645
- let statusMessage: string ;
2646
- for (const listItem of fileData) {
2647
- statusMessage = listItem.status;
2648
- const validationMessages: ValidationMessages = this.validatedFileSize(listItem.size);
2649
- if (validationMessages.minSize !== '' || validationMessages.maxSize !== '') {
2650
- isValid = false;
2651
- statusMessage = validationMessages.minSize !== '' ? this.localizedTexts('invalidMinFileSize') :
2652
- validationMessages.maxSize !== '' ? this.localizedTexts('invalidMaxFileSize') : statusMessage;
2653
- }
2654
- const typeValidationMessage: string = this.checkExtension(this.getFilesInArray(listItem))[0].status;
2655
- if ( typeValidationMessage === this.localizedTexts('invalidFileType')) {
2656
- isValid = false;
2657
- statusMessage = typeValidationMessage;
2658
- }
2659
- }
2660
-
2661
- if (!isValid) {
2662
- fileList.classList.add(INVALID_FILE);
2663
- statusMessage = fileData.length > 1 ? this.localizedTexts('invalidFileSelection') : statusMessage;
2664
- } else {
2665
- statusMessage = this.localizedTexts('totalFiles') + ': ' + fileData.length + ' , '
2666
- + this.localizedTexts('size') + ': ' +
2667
- this.bytesToSize(this.getFileSize(fileData));
2668
- }
2669
- return statusMessage;
2670
- }
2671
-
2672
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2673
- private formCustomFileList(fileData: FileInfo[], files: FileList): void {
2674
- this.createParentUL();
2675
- const fileList: HTMLElement = this.createElement('li', { className: FILE });
2676
- fileList.setAttribute('data-files-count', fileData.length + '');
2677
- this.setListToFileInfo(fileData, fileList);
2678
- const result: FileInfo = this.mergeFileInfo(fileData, fileList);
2679
- fileList.setAttribute('data-file-name', result.name);
2680
- this.uploadTemplateFn = this.templateComplier(this.template);
2681
- const liTempCompiler: any = this.uploadTemplateFn(result, this, 'template', this.element.id + 'Template', this.isStringTemplate, null, fileList);
2682
- if (liTempCompiler) {
2683
- const fromElements: HTMLElement[] = [].slice.call(liTempCompiler);
2684
- append(fromElements, fileList);
2685
- }
2686
- const index: number = this.listParent.querySelectorAll('li').length;
2687
- if (!fileList.classList.contains(INVALID_FILE)) {
2688
- this.createFormInput(fileData);
2689
- }
2690
- const eventArgs: RenderingEventArgs = {
2691
- element: fileList,
2692
- fileInfo: result,
2693
- index: index,
2694
- isPreload: this.isPreLoadFile(result)
2695
- };
2696
- const eventsArgs: FileListRenderingEventArgs = {
2697
- element: fileList,
2698
- fileInfo: result,
2699
- index: index,
2700
- isPreload: this.isPreLoadFile(result)
2701
- };
2702
- this.trigger('rendering', eventArgs);
2703
- this.trigger('fileListRendering', eventsArgs);
2704
- this.listParent.appendChild(fileList);
2705
- this.fileList.push(fileList);
2706
- this.renderReactTemplates();
2707
- }
2708
- /**
2709
- * Create the file list for specified files data.
2710
- *
2711
- * @param { FileInfo[] } fileData - Specifies the files data for file list creation.
2712
- * @returns {void}
2713
- */
2714
- public createFileList(fileData: FileInfo[]): void {
2715
- this.filesData = this.filesData && this.filesData.length > 0 ? this.filesData.concat(fileData) : fileData;
2716
- this.internalCreateFileList(fileData);
2717
- }
2718
-
2719
- private internalCreateFileList(fileData: FileInfo[]): void {
2720
- this.createParentUL();
2721
- if (this.template !== '' && !isNullOrUndefined(this.template)) {
2722
- if (this.isFormUpload()) {
2723
- this.uploadWrapper.classList.add(FORM_UPLOAD);
2724
- this.formCustomFileList(fileData, this.element.files);
2725
- } else {
2726
- this.createCustomfileList(fileData);
2727
- }
2728
- } else if (this.isFormUpload()) {
2729
- this.uploadWrapper.classList.add(FORM_UPLOAD);
2730
- this.formFileList(fileData, this.element.files);
2731
- } else {
2732
- for (const listItem of fileData) {
2733
- const liElement: HTMLElement = this.createElement('li', {
2734
- className: FILE,
2735
- attrs: { 'data-file-name': listItem.name, 'data-files-count': '1' }
2736
- });
2737
- const textContainer: Element = this.createElement('span', { className: TEXT_CONTAINER });
2738
- const textElement: HTMLElement = this.createElement(
2739
- 'span', { className: FILE_NAME, attrs: { 'title': listItem.name } });
2740
- textElement.innerHTML = this.getFileNameOnly(listItem.name);
2741
- const fileExtension: Element = this.createElement('span', { className: FILE_TYPE });
2742
- const fileType: string = this.getFileType(listItem.name);
2743
- fileExtension.innerHTML = '.' + fileType;
2744
- if (!fileType) {
2745
- fileExtension.classList.add('e-hidden');
2746
- }
2747
- if (!this.enableRtl) {
2748
- textContainer.appendChild(textElement);
2749
- textContainer.appendChild(fileExtension);
2750
- } else {
2751
- const rtlContainer: Element = this.createElement('span', { className: RTL_CONTAINER });
2752
- rtlContainer.appendChild(fileExtension);
2753
- rtlContainer.appendChild(textElement);
2754
- textContainer.appendChild(rtlContainer);
2755
- }
2756
- const fileSize: Element = this.createElement('span', { className: FILE_SIZE });
2757
- fileSize.innerHTML = this.bytesToSize(listItem.size);
2758
- textContainer.appendChild(fileSize);
2759
- const statusElement: HTMLElement = this.createElement('span', { className: STATUS });
2760
- textContainer.appendChild(statusElement);
2761
- statusElement.innerHTML = listItem.status;
2762
- liElement.appendChild(textContainer);
2763
- const iconElement: HTMLElement = this.createElement('span', { className: ' e-icons',
2764
- attrs: { 'tabindex': this.btnTabIndex } });
2765
- /* istanbul ignore next */
2766
- if (this.browserName === 'msie') {
2767
- iconElement.classList.add('e-msie');
2768
- }
2769
- iconElement.setAttribute('title', this.localizedTexts('remove'));
2770
- liElement.appendChild(iconElement);
2771
- EventHandler.add(iconElement, 'click', this.removeFiles, this);
2772
- if (listItem.statusCode === '2') {
2773
- statusElement.classList.add(UPLOAD_SUCCESS);
2774
- iconElement.classList.add(DELETE_ICON);
2775
- iconElement.setAttribute('title', this.localizedTexts('delete'));
2776
- iconElement.setAttribute('aria-label', this.localizedTexts('delete'));
2777
- } else if (listItem.statusCode !== '1') {
2778
- statusElement.classList.remove(UPLOAD_SUCCESS);
2779
- statusElement.classList.add(VALIDATION_FAILS);
2780
- }
2781
- if (this.autoUpload && listItem.statusCode === '1' && this.asyncSettings.saveUrl !== '') {
2782
- statusElement.innerHTML = '';
2783
- }
2784
- if (!iconElement.classList.contains(DELETE_ICON)) {
2785
- iconElement.classList.add(REMOVE_ICON);
2786
- iconElement.setAttribute('aria-label', this.localizedTexts('remove'));
2787
- }
2788
- const index: number = fileData.indexOf(listItem);
2789
- const eventArgs: RenderingEventArgs = {
2790
- element: liElement,
2791
- fileInfo: listItem,
2792
- index: index,
2793
- isPreload: this.isPreLoadFile(listItem)
2794
- };
2795
- const eventsArgs: FileListRenderingEventArgs = {
2796
- element: liElement,
2797
- fileInfo: listItem,
2798
- index: index,
2799
- isPreload: this.isPreLoadFile(listItem)
2800
- };
2801
- this.trigger('rendering', eventArgs);
2802
- this.trigger('fileListRendering', eventsArgs);
2803
- this.listParent.appendChild(liElement);
2804
- this.fileList.push(liElement);
2805
- this.truncateName(textElement);
2806
- const preventActionComplete: boolean = this.flag;
2807
- if (this.isPreLoadFile(listItem)) {
2808
- this.flag = false;
2809
- this.checkActionComplete(true);
2810
- this.flag = preventActionComplete;
2811
- }
2812
- }
2813
- }
2814
- }
2815
-
2816
- private getSlicedName(nameElement: HTMLElement): void {
2817
- const text: string = nameElement.textContent;
2818
- nameElement.dataset.tail = text.slice(text.length - 10);
2819
- }
2820
-
2821
- private setListToFileInfo(fileData: FileInfo[], fileList: HTMLElement): void {
2822
- for (const listItem of fileData) {
2823
- listItem.list = fileList;
2824
- }
2825
- }
2826
-
2827
- private truncateName(name: HTMLElement): void {
2828
- const nameElement: HTMLElement = name;
2829
- if (this.browserName !== 'edge' && nameElement.offsetWidth < nameElement.scrollWidth) {
2830
- this.getSlicedName(nameElement);
2831
- /* istanbul ignore next */
2832
- } else if (nameElement.offsetWidth + 1 < nameElement.scrollWidth) {
2833
- this.getSlicedName(nameElement);
2834
- }
2835
- }
2836
-
2837
- private getFileType(name: string): string {
2838
- let extension: string;
2839
- const index: number = name.lastIndexOf('.');
2840
- if (index >= 0) {
2841
- extension = name.substring(index + 1);
2842
- }
2843
- return extension ? extension : '';
2844
- }
2845
-
2846
- private getFileNameOnly(name: string): string {
2847
- let type: string = this.getFileType(name);
2848
- const names: string[] = name.split('.' + type);
2849
- return type = names[0];
2850
- }
2851
-
2852
- private setInitialAttributes(): void {
2853
- if (this.initialAttr.accept) {
2854
- this.element.setAttribute('accept', this.initialAttr.accept);
2855
- }
2856
- if (this.initialAttr.disabled) {
2857
- this.element.setAttribute('disabled', 'disabled');
2858
- }
2859
- if (this.initialAttr.multiple) {
2860
- const newAttr: Attr = document.createAttribute('multiple');
2861
- this.element.setAttributeNode(newAttr);
2862
- }
2863
- }
2864
-
2865
- private filterfileList(files : FileInfo[]) : FileInfo[] {
2866
- const filterFiles : FileInfo[] = [];
2867
- let li : HTMLElement;
2868
- for (let i : number = 0; i < files.length; i++) {
2869
- li = this.getLiElement(files[i as number]);
2870
- if (!isNullOrUndefined(li) && !li.classList.contains(UPLOAD_SUCCESS)) {
2871
- filterFiles.push(files[i as number]);
2872
- }
2873
- else if (!this.showFileList && files[i as number].status !== 'File uploaded successfully') {
2874
- filterFiles.push(files[i as number]);
2875
- }
2876
- }
2877
- return filterFiles;
2878
- }
2879
-
2880
- private updateStatus(files : FileInfo, status ? : string, statusCode? : string, updateLiStatus : boolean = true ) : FileInfo {
2881
- if (!(status === '' || isNullOrUndefined(status)) && !(statusCode === '' || isNullOrUndefined(statusCode))) {
2882
- files.status = status;
2883
- files.statusCode = statusCode;
2884
- }
2885
- if (updateLiStatus) {
2886
- const li : HTMLElement = this.getLiElement(files);
2887
- if (!isNullOrUndefined(li)) {
2888
- if (!isNullOrUndefined(li.querySelector('.' + STATUS)) && !((status === '' || isNullOrUndefined(status)))) {
2889
- li.querySelector('.' + STATUS).textContent = status;
2890
- }
2891
- }
2892
- }
2893
- return files;
2894
- }
2895
-
2896
- private getLiElement(files : FileInfo) : HTMLElement {
2897
- let index: number;
2898
- for (let i: number = 0; i < this.filesData.length; i++) {
2899
- if (!isNullOrUndefined(files) && ((!isNullOrUndefined(this.filesData[i as number].id) &&
2900
- !isNullOrUndefined(files.id)) ? (this.filesData[i as number].name === files.name &&
2901
- this.filesData[i as number].id === files.id) : this.filesData[i as number].name === files.name)) {
2902
- index = i;
2903
- }
2904
- }
2905
- return this.fileList[index as number];
2906
- }
2907
-
2908
- private createProgressBar(liElement : Element) : void {
2909
- const progressbarWrapper : Element = this.createElement('span', {className: PROGRESS_WRAPPER});
2910
- const progressBar : Element = this.createElement('progressbar', {className: PROGRESSBAR, attrs: {value : '0', max : '100'}});
2911
- const progressbarInnerWrapper : Element = this.createElement('span', {className: PROGRESS_INNER_WRAPPER});
2912
- progressBar.setAttribute('style', 'width: 0%');
2913
- const progressbarText : Element = this.createElement('span', {className: PROGRESSBAR_TEXT});
2914
- progressbarText.textContent = '0%';
2915
- progressbarInnerWrapper.appendChild(progressBar);
2916
- progressbarWrapper.appendChild(progressbarInnerWrapper);
2917
- progressbarWrapper.appendChild(progressbarText);
2918
- liElement.querySelector('.' + TEXT_CONTAINER).appendChild(progressbarWrapper);
2919
- }
2920
-
2921
- /* istanbul ignore next */
2922
- private updateProgressbar(e : ProgressEventInit, li : Element) : void {
2923
- if (!isNaN(Math.round((e.loaded / e.total) * 100)) && !isNullOrUndefined(li.querySelector('.' + PROGRESSBAR))) {
2924
- if (!isNullOrUndefined(this.progressInterval) && this.progressInterval !== '') {
2925
- const value : number = (Math.round((e.loaded / e.total) * 100)) % parseInt(this.progressInterval, 10);
2926
- if (value === 0 || value === 100 ) {
2927
- this.changeProgressValue(li, Math.round((e.loaded / e.total) * 100).toString() + '%');
2928
- }
2929
- } else {
2930
- this.changeProgressValue(li, Math.round((e.loaded / e.total) * 100).toString() + '%');
2931
- }
2932
- }
2933
- }
2934
-
2935
- private changeProgressValue(li : Element, progressValue : string) : void {
2936
- li.querySelector('.' + PROGRESSBAR).setAttribute('style', 'width:' + progressValue );
2937
- li.querySelector('.' + PROGRESSBAR_TEXT).textContent = progressValue;
2938
- }
2939
-
2940
- private uploadInProgress (e: ProgressEventInit , files : FileInfo, customUI?: boolean, request?: Ajax) : void {
2941
- const li : HTMLElement = this.getLiElement(files);
2942
- if (isNullOrUndefined(li) && (!customUI)) {
2943
- return;
2944
- }
2945
- if (!isNullOrUndefined(li)) {
2946
- /* istanbul ignore next */
2947
- if (files.statusCode === '5') {
2948
- this.cancelUploadingFile(files, e, request, li);
2949
- }
2950
- if (!(li.querySelectorAll('.' + PROGRESS_WRAPPER).length > 0) && li.querySelector('.' + STATUS)) {
2951
- li.querySelector('.' + STATUS).classList.add(UPLOAD_INPROGRESS);
2952
- this.createProgressBar(li);
2953
- this.updateProgressBarClasses(li, UPLOAD_INPROGRESS);
2954
- li.querySelector('.' + STATUS).classList.remove(UPLOAD_FAILED);
2955
- }
2956
- this.updateProgressbar(e, <Element>li);
2957
- const iconEle: HTMLElement = li.querySelector('.' + REMOVE_ICON) as HTMLElement;
2958
- if (!isNullOrUndefined(iconEle)) {
2959
- iconEle.classList.add(ABORT_ICON, UPLOAD_INPROGRESS);
2960
- iconEle.setAttribute('title', this.localizedTexts('abort'));
2961
- iconEle.classList.remove(REMOVE_ICON);
2962
- }
2963
- } else {
2964
- this.cancelUploadingFile(files, e, request);
2965
- }
2966
- const args : object = {e, operation: 'upload', file: this.updateStatus(files, this.localizedTexts('inProgress'), '3')};
2967
- this.trigger('progress', args);
2968
- }
2969
-
2970
- /* istanbul ignore next */
2971
- private cancelUploadingFile(files: FileInfo, e: ProgressEventInit, request?: Ajax, li?: HTMLElement): void {
2972
- if (files.statusCode === '5') {
2973
- const eventArgs: CancelEventArgs = {
2974
- event: e,
2975
- fileData: files,
2976
- cancel: false,
2977
- customFormData : [],
2978
- currentRequest : null
2979
- };
2980
- this.trigger('canceling', eventArgs, (eventArgs: CancelEventArgs) => {
2981
- if (eventArgs.cancel) {
2982
- files.statusCode = '3';
2983
- if (!isNullOrUndefined(li)) {
2984
- const spinnerTarget: HTMLElement = li.querySelector('.' + ABORT_ICON) as HTMLElement;
2985
- if (!isNullOrUndefined(spinnerTarget)) {
2986
- hideSpinner(spinnerTarget);
2987
- detach(li.querySelector('.e-spinner-pane'));
2988
- }
2989
- }
2990
- } else {
2991
- request.emitError = false;
2992
- request.httpRequest.abort();
2993
- const formData: FormData = new FormData();
2994
- if (files.statusCode === '5') {
2995
- const name: string = this.element.getAttribute('name');
2996
- formData.append(name, files.name);
2997
- formData.append('cancel-uploading', files.name);
2998
- this.updateFormData(formData, eventArgs.customFormData);
2999
- const ajax: Ajax = new Ajax(this.asyncSettings.removeUrl, 'POST', true, null);
3000
- ajax.emitError = false;
3001
- ajax.beforeSend = (e: BeforeSendEventArgs) => {
3002
- if (eventArgs.currentRequest) {
3003
- this.updateCustomheader(ajax.httpRequest, eventArgs.currentRequest);
3004
- }
3005
- };
3006
- ajax.onLoad = (e: Event): object => {
3007
- this.removecanceledFile(e, files); return {};
3008
- };
3009
- ajax.send(formData);
3010
- }
3011
- }
3012
- });
3013
- }
3014
- }
3015
-
3016
- private removecanceledFile(e: Event, file: FileInfo): void {
3017
- const liElement: HTMLElement = this.getLiElement(file);
3018
- if (isNullOrUndefined(liElement) || liElement.querySelector('.' + RETRY_ICON) || isNullOrUndefined(liElement.querySelector('.' + ABORT_ICON))) {
3019
- return;
3020
- }
3021
- this.updateStatus(file, this.localizedTexts('fileUploadCancel'), '5');
3022
- this.renderFailureState(e, file, liElement);
3023
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + REMOVE_ICON ) as HTMLElement;
3024
- if (!isNullOrUndefined(liElement)) {
3025
- hideSpinner(spinnerTarget);
3026
- if (!isNullOrUndefined(liElement.querySelector('.e-spinner-pane'))) {
3027
- detach(liElement.querySelector('.e-spinner-pane'));
3028
- }
3029
- }
3030
- const requestResponse: Object = e && e.currentTarget ? this.getResponse(e) : null;
3031
- const args: Object = { event: e, response: requestResponse, operation: 'cancel', file: file };
3032
- this.trigger('success', args);
3033
- }
3034
-
3035
- private renderFailureState(e: Event, file: FileInfo, liElement: HTMLElement): void {
3036
- this.updateProgressBarClasses(liElement, UPLOAD_FAILED);
3037
- this.removeProgressbar(liElement, 'failure');
3038
- if (!isNullOrUndefined(liElement.querySelector('.e-file-status'))) {
3039
- liElement.querySelector('.e-file-status').classList.add(UPLOAD_FAILED);
3040
- }
3041
- const deleteIcon: Element = liElement.querySelector('.' + ABORT_ICON);
3042
- if (isNullOrUndefined(deleteIcon)) {
3043
- return;
3044
- }
3045
- deleteIcon.classList.remove(ABORT_ICON, UPLOAD_INPROGRESS);
3046
- deleteIcon.classList.add(REMOVE_ICON);
3047
- deleteIcon.setAttribute('title', this.localizedTexts('remove'));
3048
- this.pauseButton = this.createElement('span', {className: 'e-icons e-file-reload-btn', attrs: { 'tabindex': this.btnTabIndex}});
3049
- deleteIcon.parentElement.insertBefore(this.pauseButton, deleteIcon);
3050
- this.pauseButton.setAttribute('title', this.localizedTexts('retry'));
3051
- this.pauseButton.setAttribute('aria-label', this.localizedTexts('retry'));
3052
- const retryElement: HTMLElement = liElement.querySelector('.' + RETRY_ICON) as HTMLElement;
3053
- /* istanbul ignore next */
3054
- retryElement.addEventListener('click', (e: Event) => {
3055
- this.reloadcanceledFile(e, file, liElement, false);
3056
- }, false);
3057
- }
3058
-
3059
- private reloadcanceledFile(e: Event, file: FileInfo, liElement?: HTMLElement, custom?: boolean): void {
3060
- file.statusCode = '1';
3061
- file.status = this.localizedTexts('readyToUploadMessage');
3062
- if (!custom) {
3063
- if (!isNullOrUndefined(liElement.querySelector('.' + STATUS))) {
3064
- liElement.querySelector('.' + STATUS).classList.remove(UPLOAD_FAILED);
3065
- }
3066
- if (!isNullOrUndefined(liElement.querySelector('.' + RETRY_ICON))) {
3067
- detach(liElement.querySelector('.' + RETRY_ICON));
3068
- }
3069
- this.pauseButton = null;
3070
- }
3071
- /* istanbul ignore next */
3072
- if (!isNullOrUndefined(liElement)) {
3073
- liElement.classList.add(RESTRICT_RETRY);
3074
- }
3075
- this.upload([file]);
3076
- }
3077
-
3078
- /* istanbul ignore next */
3079
- private uploadComplete(e: Event, file: FileInfo, customUI ?: boolean) : void {
3080
- const status : XMLHttpRequest = e.target as XMLHttpRequest;
3081
- if (status.readyState === 4 && status.status >= 200 && status.status <= 299 ) {
3082
- const li : HTMLElement = this.getLiElement(file);
3083
- if (isNullOrUndefined(li) && (!customUI || isNullOrUndefined(customUI)) && this.showFileList ) {
3084
- return;
3085
- }
3086
- if (!isNullOrUndefined(li)) {
3087
- this.updateProgressBarClasses(li, UPLOAD_SUCCESS);
3088
- this.removeProgressbar(li, 'success');
3089
- const iconEle: Element = li.querySelector('.' + ABORT_ICON);
3090
- if (!isNullOrUndefined(iconEle)) {
3091
- iconEle.classList.add(DELETE_ICON);
3092
- iconEle.setAttribute('title', this.localizedTexts('delete'));
3093
- iconEle.setAttribute('aria-label', this.localizedTexts('delete'));
3094
- iconEle.classList.remove(ABORT_ICON);
3095
- iconEle.classList.remove(UPLOAD_INPROGRESS);
3096
- }
3097
- }
3098
- this.raiseSuccessEvent(e, file);
3099
- } else {
3100
- this.uploadFailed(e, file);
3101
- }
3102
- }
3103
-
3104
- private getResponse(e: Event): Object {
3105
- const target: any = e.currentTarget;
3106
- const response: Object = {
3107
- readyState: target.readyState,
3108
- statusCode: target.status,
3109
- statusText: target.statusText,
3110
- headers: target.getAllResponseHeaders(),
3111
- withCredentials: target.withCredentials
3112
- };
3113
- return response;
3114
- }
3115
-
3116
- private raiseSuccessEvent(e: Event, file: FileInfo): void {
3117
- const response: Object = e && e.currentTarget ? this.getResponse(e) : null;
3118
- const statusMessage: string = this.localizedTexts('uploadSuccessMessage');
3119
- const args: Object = {
3120
- e, response: response, operation: 'upload', file: this.updateStatus(file, statusMessage, '2', false), statusText: statusMessage
3121
- };
3122
- const liElement: HTMLElement = this.getLiElement(file);
3123
- if (!isNullOrUndefined(liElement)) {
3124
- const spinnerEle: HTMLElement = liElement.querySelector('.' + SPINNER_PANE) as HTMLElement;
3125
- if (!isNullOrUndefined(spinnerEle)) {
3126
- hideSpinner(liElement);
3127
- detach(spinnerEle);
3128
- }
3129
- }
3130
- this.trigger('success', args, (args: Object) => {
3131
- this.updateStatus(file, (args as any).statusText, '2');
3132
- if (this.multiple) {
3133
- this.uploadedFilesData.push(file);
3134
- }
3135
- else {
3136
- this.uploadedFilesData = [file];
3137
- }
3138
- this.trigger('change', { file: this.uploadedFilesData });
3139
- this.checkActionButtonStatus();
3140
- if (this.fileList.length > 0) {
3141
- if ((!(this.getLiElement(file)).classList.contains(RESTRICT_RETRY))) {
3142
- this.uploadSequential();
3143
- this.checkActionComplete(true);
3144
- } else {
3145
- /* istanbul ignore next */
3146
- (this.getLiElement(file)).classList.remove(RESTRICT_RETRY);
3147
- }
3148
- }
3149
- else if (!this.showFileList) {
3150
- this.checkActionComplete(true);
3151
- }
3152
- });
3153
- }
3154
-
3155
- private uploadFailed(e: Event, file: FileInfo): void {
3156
- const li: HTMLElement = this.getLiElement(file);
3157
- const response: Object = e && e.currentTarget ? this.getResponse(e) : null;
3158
- const statusMessage: string = this.localizedTexts('uploadFailedMessage');
3159
- const args: Object = {
3160
- e, response: response, operation: 'upload', file: this.updateStatus(file, statusMessage, '0', false), statusText: statusMessage
3161
- };
3162
- if (!isNullOrUndefined(li)) {
3163
- this.renderFailureState(e, file, li);
3164
- }
3165
- this.trigger('failure', args, (args: Object) => {
3166
- this.updateStatus(file, (args as any).statusText, '0');
3167
- this.checkActionButtonStatus();
3168
- this.uploadSequential();
3169
- this.checkActionComplete(true);
3170
- });
3171
- }
3172
-
3173
- private uploadSequential() : void {
3174
- if (this.sequentialUpload) {
3175
- if (this.autoUpload) {
3176
- /* istanbul ignore next */
3177
- this.checkAutoUpload(this.filesData);
3178
- } else {
3179
- this.uploadButtonClick();
3180
- }
3181
- }
3182
- }
3183
-
3184
- private checkActionComplete(increment: boolean): void {
3185
- if (increment) {
3186
- ++this.actionCompleteCount;
3187
- } else {
3188
- --this.actionCompleteCount;
3189
- }
3190
- this.raiseActionComplete();
3191
- }
3192
-
3193
- private raiseActionComplete(): void {
3194
- if ((this.filesData.length === this.actionCompleteCount) && this.flag) {
3195
- this.flag = false;
3196
- const eventArgs: ActionCompleteEventArgs = {
3197
- fileData: []
3198
- };
3199
- eventArgs.fileData = this.getSelectedFileStatus(this.selectedFiles);
3200
- this.trigger('actionComplete', eventArgs);
3201
- }
3202
- }
3203
-
3204
- private getSelectedFileStatus(selectedFiles: FileInfo[]): FileInfo[] {
3205
- const matchFiles: FileInfo [] = [];
3206
- let matchFilesIndex: number = 0;
3207
- for ( let selectFileIndex: number = 0 ; selectFileIndex < selectedFiles.length; selectFileIndex++ ) {
3208
- const selectedFileData: FileInfo = selectedFiles[selectFileIndex as number];
3209
- for ( let fileDataIndex: number = 0 ; fileDataIndex < this.filesData.length; fileDataIndex++ ) {
3210
- if ( this.filesData[fileDataIndex as number].name === selectedFileData.name &&
3211
- this.filesData[fileDataIndex as number].status === selectedFileData.status ) {
3212
- matchFiles[matchFilesIndex as number] = this.filesData[fileDataIndex as number];
3213
- ++matchFilesIndex;
3214
- break;
3215
- }
3216
- }
3217
- }
3218
- return matchFiles;
3219
- }
3220
-
3221
- private updateProgressBarClasses (li : HTMLElement, className : string) : void {
3222
- const progressBar: HTMLElement = <HTMLElement>li.querySelector('.' + PROGRESSBAR);
3223
- if (!isNullOrUndefined(progressBar)) {
3224
- progressBar.classList.add(className);
3225
- if (this.actionButtons && this.clearButton && this.uploadWrapper) {
3226
- if (className === 'e-upload-progress' && !this.clearButton.hasAttribute('disabled')) {
3227
- this.clearButton.setAttribute('disabled', 'disabled');
3228
- } else if ((className === 'e-upload-success' || className === 'e-upload-fails') && this.uploadWrapper.querySelectorAll(
3229
- '.e-upload-progress-bar.e-upload-progress:not(.e-upload-success):not(.e-upload-fails)').length === 0 &&
3230
- this.clearButton.hasAttribute('disabled')) {
3231
- this.clearButton.removeAttribute('disabled');
3232
- }
3233
- }
3234
- }
3235
- }
3236
-
3237
- private removeProgressbar(li : HTMLElement, callType : string) : void {
3238
- if (!isNullOrUndefined(li.querySelector('.' + PROGRESS_WRAPPER))) {
3239
- this.progressAnimation = new Animation({ duration: 1250 });
3240
- this.progressAnimation.animate(<HTMLElement>li.querySelector('.' + PROGRESS_WRAPPER), {name: 'FadeOut'});
3241
- this.progressAnimation.animate(<HTMLElement>li.querySelector('.' + PROGRESSBAR_TEXT), {name: 'FadeOut'});
3242
- setTimeout(() => {
3243
- this.animateProgressBar(li, callType);
3244
- }, 750);
3245
- }
3246
- }
3247
-
3248
- /* istanbul ignore next */
3249
- private animateProgressBar(li: Element, callType: string) : void {
3250
- if (callType === 'success') {
3251
- li.classList.add(UPLOAD_SUCCESS);
3252
- if (!isNullOrUndefined(li.querySelector('.' + STATUS))) {
3253
- li.querySelector('.' + STATUS).classList.remove(UPLOAD_INPROGRESS);
3254
- this.progressAnimation.animate(<HTMLElement>li.querySelector('.' + STATUS), {name: 'FadeIn'});
3255
- li.querySelector('.' + STATUS).classList.add(UPLOAD_SUCCESS);
3256
- }
3257
- } else {
3258
- if (!isNullOrUndefined(li.querySelector('.' + STATUS))) {
3259
- li.querySelector('.' + STATUS).classList.remove(UPLOAD_INPROGRESS);
3260
- this.progressAnimation.animate(<HTMLElement>li.querySelector('.' + STATUS), {name: 'FadeIn'});
3261
- li.querySelector('.' + STATUS).classList.add(UPLOAD_FAILED);
3262
- }
3263
- }
3264
- if (li.querySelector('.' + PROGRESS_WRAPPER)) {
3265
- detach(li.querySelector('.' + PROGRESS_WRAPPER));
3266
- }
3267
- }
3268
-
3269
- private setExtensions(extensions : string): void {
3270
- if (extensions !== '' && !isNullOrUndefined(extensions)) {
3271
- this.element.setAttribute('accept', extensions);
3272
- } else {
3273
- this.element.removeAttribute('accept');
3274
- }
3275
- }
3276
-
3277
- private templateComplier(uploadTemplate: string | Function): Function {
3278
- if (uploadTemplate) {
3279
- try {
3280
- if (typeof uploadTemplate !== 'function' && selectAll(uploadTemplate, document).length) {
3281
- return compile(select(uploadTemplate, document).innerHTML.trim());
3282
- } else {
3283
- return compile(uploadTemplate);
3284
- }
3285
- } catch (exception) {
3286
- return compile(uploadTemplate);
3287
- }
3288
- }
3289
- return undefined;
3290
- }
3291
-
3292
- private setRTL() : void {
3293
- if (this.enableRtl) {
3294
- addClass([this.uploadWrapper], RTL);
3295
- } else {
3296
- removeClass([this.uploadWrapper], RTL);
3297
- }
3298
- }
3299
-
3300
- private localizedTexts(localeText : string): string {
3301
- this.l10n.setLocale(this.locale);
3302
- return this.l10n.getConstant(localeText);
3303
- }
3304
-
3305
- private setControlStatus() : void {
3306
- if (!this.enabled) {
3307
- this.uploadWrapper.classList.add(DISABLED);
3308
- this.element.setAttribute('disabled', 'disabled');
3309
- this.browseButton.setAttribute('disabled', 'disabled');
3310
- if (!isNullOrUndefined(this.clearButton)) {
3311
- this.clearButton.setAttribute('disabled', 'disabled');
3312
- }
3313
- if (!isNullOrUndefined(this.uploadButton)) {
3314
- this.uploadButton.setAttribute('disabled', 'disabled');
3315
- }
3316
- } else {
3317
- if (this.uploadWrapper.classList.contains(DISABLED)) {
3318
- this.uploadWrapper.classList.remove(DISABLED);
3319
- }
3320
- if (!isNullOrUndefined(this.browseButton) && this.element.hasAttribute('disabled')) {
3321
- this.element.removeAttribute('disabled');
3322
- this.browseButton.removeAttribute('disabled');
3323
- }
3324
- if (!isNullOrUndefined(this.clearButton) && this.clearButton.hasAttribute('disabled')) {
3325
- this.clearButton.removeAttribute('disabled');
3326
- }
3327
- if (!isNullOrUndefined(this.uploadButton) && this.uploadButton.hasAttribute('disabled')) {
3328
- this.uploadButton.hasAttribute('disabled');
3329
- }
3330
- }
3331
- }
3332
-
3333
- private checkHTMLAttributes(isDynamic: boolean) : void {
3334
- const attributes: string[] = isDynamic ? isNullOrUndefined(this.htmlAttributes) ? [] : Object.keys(this.htmlAttributes) :
3335
- ['accept', 'multiple', 'disabled'];
3336
- for (const prop of attributes) {
3337
- if (!isNullOrUndefined(this.element.getAttribute(prop))) {
3338
- switch (prop) {
3339
- case 'accept':
3340
- if ((isNullOrUndefined(this.uploaderOptions) || (this.uploaderOptions['allowedExtensions'] === undefined))
3341
- || isDynamic) {
3342
- this.setProperties({allowedExtensions: this.element.getAttribute('accept')}, !isDynamic);
3343
- this.initialAttr.accept = this.allowedExtensions;
3344
- }
3345
- break;
3346
- case 'multiple':
3347
- if ((isNullOrUndefined(this.uploaderOptions) || (this.uploaderOptions['multiple'] === undefined)) || isDynamic) {
3348
- const isMutiple: boolean = this.element.getAttribute(prop) === 'multiple' ||
3349
- this.element.getAttribute(prop) === '' || this.element.getAttribute(prop) === 'true' ? true : false;
3350
- this.setProperties({multiple: isMutiple}, !isDynamic);
3351
- this.initialAttr.multiple = true;
3352
- }
3353
- break;
3354
- case 'disabled':
3355
- if ((isNullOrUndefined(this.uploaderOptions) || (this.uploaderOptions['enabled'] === undefined)) || isDynamic) {
3356
- const isDisabled: boolean = this.element.getAttribute(prop) === 'disabled' ||
3357
- this.element.getAttribute(prop) === '' || this.element.getAttribute(prop) === 'true' ? false : true;
3358
- this.setProperties({enabled: isDisabled}, !isDynamic);
3359
- this.initialAttr.disabled = true;
3360
- }
3361
- }
3362
- }
3363
- }
3364
- }
3365
-
3366
- private chunkUpload(file: FileInfo, custom?: boolean, fileIndex?: number): void {
3367
- const start: number = 0;
3368
- const end: number = Math.min(this.asyncSettings.chunkSize, file.size);
3369
- const index: number = 0;
3370
- const blob: string | Blob = file.rawFile.slice(start, end);
3371
- const metaData: MetaData = { chunkIndex: index, blob: blob, file: file, start: start, end: end, retryCount: 0, request: null };
3372
- this.sendRequest(file, metaData, custom, fileIndex);
3373
- }
3374
-
3375
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3376
- private sendRequest(file: FileInfo, metaData: MetaData, custom?: boolean, fileIndex?: number): void {
3377
- const formData: FormData = new FormData();
3378
- const blob: string | Blob = file.rawFile.slice(metaData.start, metaData.end);
3379
- formData.append(this.uploaderName, blob, file.name);
3380
- formData.append('chunk-index', metaData.chunkIndex.toString());
3381
- formData.append('chunkIndex', metaData.chunkIndex.toString());
3382
- const totalChunk: number = Math.max(Math.ceil(file.size / this.asyncSettings.chunkSize), 1);
3383
- formData.append('total-chunk', totalChunk.toString());
3384
- formData.append('totalChunk', totalChunk.toString());
3385
- const ajax: Ajax = new Ajax({ url: this.asyncSettings.saveUrl, type: 'POST', async: true, contentType: null });
3386
- ajax.emitError = false;
3387
- ajax.onLoad = (e: Event): object => {
3388
- this.chunkUploadComplete(e, metaData, custom); return {};
3389
- };
3390
- ajax.onUploadProgress = (e: Event) => {
3391
- this.chunkUploadInProgress(e, metaData, custom);
3392
- return {};
3393
- };
3394
- const eventArgs: UploadingEventArgs = {
3395
- fileData: file,
3396
- customFormData: [],
3397
- cancel: false,
3398
- chunkSize: this.asyncSettings.chunkSize === 0 ? null : this.asyncSettings.chunkSize
3399
- };
3400
- ajax.beforeSend = (e: BeforeSendEventArgs) => {
3401
- eventArgs.currentRequest = ajax.httpRequest;
3402
- eventArgs.currentChunkIndex = metaData.chunkIndex;
3403
- if (eventArgs.currentChunkIndex === 0) {
3404
- // This event is currently not required but to avoid breaking changes for previous customer, we have included.
3405
- this.trigger('uploading', eventArgs, (eventArgs: UploadingEventArgs) => {
3406
- this.uploadingEventCallback(formData, eventArgs, e, file);
3407
- });
3408
- } else {
3409
- this.trigger('chunkUploading', eventArgs, (eventArgs: UploadingEventArgs) => {
3410
- this.uploadingEventCallback(formData, eventArgs, e, file);
3411
- });
3412
- }
3413
- };
3414
- /* istanbul ignore next */
3415
- ajax.onError = (e: Event) => {
3416
- this.chunkUploadFailed(e, metaData, custom); return {};
3417
- };
3418
- ajax.send(formData);
3419
- metaData.request = ajax;
3420
- }
3421
-
3422
- private uploadingEventCallback(formData: FormData, eventArgs: UploadingEventArgs, e: BeforeSendEventArgs, file: FileInfo): void {
3423
- if (eventArgs.cancel) {
3424
- this.eventCancelByArgs(e, eventArgs, file);
3425
- } else {
3426
- this.updateFormData(formData, eventArgs.customFormData);
3427
- }
3428
- }
3429
-
3430
- private eventCancelByArgs(e: BeforeSendEventArgs, eventArgs: UploadingEventArgs, file: FileInfo): void {
3431
- e.cancel = true;
3432
- if (eventArgs.fileData.statusCode === '5') {
3433
- return;
3434
- }
3435
- eventArgs.fileData.statusCode = '5';
3436
- eventArgs.fileData.status = this.localizedTexts('fileUploadCancel');
3437
- const liElement: HTMLElement = this.getLiElement(eventArgs.fileData);
3438
- if (liElement) {
3439
- if (!isNullOrUndefined(liElement.querySelector('.' + STATUS))) {
3440
- liElement.querySelector('.' + STATUS).innerHTML = this.localizedTexts('fileUploadCancel');
3441
- liElement.querySelector('.' + STATUS).classList.add(UPLOAD_FAILED);
3442
- }
3443
- this.pauseButton = this.createElement('span',
3444
- {className: 'e-icons e-file-reload-btn', attrs: { 'tabindex': this.btnTabIndex}});
3445
- const removeIcon: Element = liElement.querySelector('.' + REMOVE_ICON);
3446
- if (removeIcon) {
3447
- removeIcon.parentElement.insertBefore(this.pauseButton, removeIcon);
3448
- }
3449
- this.pauseButton.setAttribute('title', this.localizedTexts('retry'));
3450
- /* istanbul ignore next */
3451
- this.pauseButton.addEventListener('click', (e: Event) => {
3452
- this.reloadcanceledFile(e, file, liElement);
3453
- }, false);
3454
- this.checkActionButtonStatus();
3455
- }
3456
- }
3457
-
3458
- private checkChunkUpload(): boolean {
3459
- return (this.asyncSettings.chunkSize <= 0 || isNullOrUndefined(this.asyncSettings.chunkSize)) ? false : true;
3460
- }
3461
-
3462
- private chunkUploadComplete(e: Event, metaData: MetaData, custom?: boolean): void {
3463
- const response: XMLHttpRequest = e.target as XMLHttpRequest;
3464
- let liElement: HTMLElement;
3465
- if (response.readyState === 4 && response.status >= 200 && response.status < 300) {
3466
- const requestResponse: Object = e && e.currentTarget ? this.getResponse(e) : null;
3467
- const totalChunk: number = Math.max(Math.ceil(metaData.file.size / this.asyncSettings.chunkSize), 1);
3468
- const eventArgs: Object = {
3469
- event: e,
3470
- file: metaData.file,
3471
- chunkIndex: metaData.chunkIndex,
3472
- totalChunk: totalChunk,
3473
- chunkSize: this.asyncSettings.chunkSize,
3474
- response: requestResponse
3475
- };
3476
- this.trigger('chunkSuccess', eventArgs);
3477
- if (isNullOrUndefined(custom) || !custom) {
3478
- liElement = this.getLiElement(metaData.file);
3479
- }
3480
- this.updateMetaData(metaData);
3481
- if (metaData.end === metaData.file.size) {
3482
- metaData.file.statusCode = '3';
3483
- }
3484
- if (metaData.file.statusCode === '5') {
3485
- const eventArgs: CancelEventArgs = { event: e, fileData: metaData.file, cancel: false, customFormData: [] };
3486
- this.trigger('canceling', eventArgs, (eventArgs: CancelEventArgs) => {
3487
- /* istanbul ignore next */
3488
- if (eventArgs.cancel) {
3489
- metaData.file.statusCode = '3';
3490
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + ABORT_ICON) as HTMLElement;
3491
- if (!isNullOrUndefined(liElement) && !isNullOrUndefined(spinnerTarget)) {
3492
- hideSpinner(spinnerTarget);
3493
- detach(liElement.querySelector('.e-spinner-pane'));
3494
- }
3495
- this.sendNextRequest(metaData);
3496
- } else {
3497
- metaData.request.emitError = false;
3498
- response.abort();
3499
- const formData: FormData = new FormData();
3500
- const name: string = this.element.getAttribute('name');
3501
- formData.append(name, metaData.file.name);
3502
- formData.append('cancel-uploading', metaData.file.name);
3503
- formData.append('cancelUploading', metaData.file.name);
3504
- this.updateFormData(formData, eventArgs.customFormData);
3505
- const ajax: Ajax = new Ajax(this.asyncSettings.removeUrl, 'POST', true, null);
3506
- ajax.emitError = false;
3507
- ajax.onLoad = (e: Event): object => {
3508
- this.removeChunkFile(e, metaData, custom); return {};
3509
- };
3510
- ajax.send(formData);
3511
- }
3512
- });
3513
- } else {
3514
- if ((totalChunk - 1) === metaData.chunkIndex && totalChunk > metaData.chunkIndex) {
3515
- const index: number = this.pausedData.indexOf(metaData);
3516
- if (index >= 0) {
3517
- this.pausedData.splice(index, 1);
3518
- }
3519
- if (isNullOrUndefined(this.template) && (isNullOrUndefined(custom) || !custom) && liElement) {
3520
- if (liElement && !isNullOrUndefined(liElement.querySelector('.' + PAUSE_UPLOAD))) {
3521
- detach(liElement.querySelector('.' + PAUSE_UPLOAD));
3522
- }
3523
- this.removeChunkProgressBar(metaData);
3524
- }
3525
- this.raiseSuccessEvent(e, metaData.file);
3526
- return;
3527
- }
3528
- if (metaData.file.statusCode !== '4') {
3529
- this.sendNextRequest(metaData);
3530
- }
3531
- }
3532
- } else {
3533
- this.chunkUploadFailed(e, metaData);
3534
- }
3535
- }
3536
-
3537
- private sendNextRequest(metaData: MetaData): void {
3538
- metaData.start = metaData.end;
3539
- metaData.end += this.asyncSettings.chunkSize;
3540
- metaData.end = Math.min(metaData.end, metaData.file.size);
3541
- metaData.chunkIndex += 1;
3542
- this.sendRequest(metaData.file, metaData);
3543
- }
3544
-
3545
- private removeChunkFile(e: Event, metaData: MetaData, custom: boolean): void {
3546
- if (isNullOrUndefined(this.template) && (isNullOrUndefined(custom) && !custom)) {
3547
- const liElement: HTMLElement = this.getLiElement(metaData.file);
3548
- const deleteIcon: Element = liElement.querySelector('.' + ABORT_ICON);
3549
- const spinnerTarget: HTMLElement = deleteIcon as HTMLElement;
3550
- this.updateStatus(metaData.file, this.localizedTexts('fileUploadCancel'), '5');
3551
- this.updateProgressBarClasses(liElement, UPLOAD_FAILED);
3552
- this.removeProgressbar(liElement, 'failure');
3553
- if (deleteIcon) {
3554
- deleteIcon.classList.remove(ABORT_ICON);
3555
- deleteIcon.classList.add(REMOVE_ICON);
3556
- deleteIcon.setAttribute('title', this.localizedTexts('remove'));
3557
- }
3558
- const pauseIcon: Element = liElement.querySelector('.' + PAUSE_UPLOAD);
3559
- if (pauseIcon) {
3560
- pauseIcon.classList.add(RETRY_ICON);
3561
- pauseIcon.classList.remove(PAUSE_UPLOAD);
3562
- pauseIcon.setAttribute('title', this.localizedTexts('retry'));
3563
- }
3564
- if (!isNullOrUndefined(liElement) && !isNullOrUndefined(deleteIcon)
3565
- && !isNullOrUndefined(liElement.querySelector('.e-spinner-pane'))) {
3566
- hideSpinner(spinnerTarget);
3567
- detach(liElement.querySelector('.e-spinner-pane'));
3568
- }
3569
- }
3570
- }
3571
-
3572
- private pauseUpload(metaData: MetaData, e?: Event, custom?: boolean): void {
3573
- metaData.file.statusCode = '4';
3574
- metaData.file.status = this.localizedTexts('pause');
3575
- this.updateMetaData(metaData);
3576
- const eventArgs: PauseResumeEventArgs = {
3577
- event: e ? e : null,
3578
- file: metaData.file,
3579
- chunkIndex: metaData.chunkIndex,
3580
- chunkCount: Math.round(metaData.file.size / this.asyncSettings.chunkSize),
3581
- chunkSize: this.asyncSettings.chunkSize
3582
- };
3583
- this.abortUpload(metaData, custom, eventArgs);
3584
- if (this.sequentialUpload) {
3585
- this.uploadSequential();
3586
- }
3587
- }
3588
-
3589
- private abortUpload(metaData: MetaData, custom: boolean, eventArgs?: PauseResumeEventArgs): void {
3590
- if (metaData.file.statusCode !== '4') {
3591
- metaData.request.emitError = false;
3592
- metaData.request.httpRequest.abort();
3593
- }
3594
- const liElement: HTMLElement = this.getLiElement(metaData.file);
3595
- if (isNullOrUndefined(this.template) && (isNullOrUndefined(custom) || !custom) && liElement) {
3596
- const targetElement: HTMLElement = liElement.querySelector('.' + PAUSE_UPLOAD) as HTMLElement;
3597
- targetElement.classList.remove(PAUSE_UPLOAD);
3598
- targetElement.classList.add(RESUME_UPLOAD);
3599
- targetElement.setAttribute('title', this.localizedTexts('resume'));
3600
- targetElement.nextElementSibling.classList.add(REMOVE_ICON);
3601
- targetElement.nextElementSibling.classList.remove(ABORT_ICON);
3602
- targetElement.nextElementSibling.setAttribute('title', this.localizedTexts('remove'));
3603
- }
3604
- for (let i: number = 0; i < this.pausedData.length; i++) {
3605
- if (this.pausedData[i as number].file.name === metaData.file.name) {
3606
- this.pausedData.splice(i, 1);
3607
- }
3608
- }
3609
- this.pausedData.push(metaData);
3610
- this.trigger('pausing', eventArgs);
3611
- }
3612
- private resumeUpload(metaData: MetaData, e?: Event, custom?: boolean): void {
3613
- const liElement: HTMLElement = this.getLiElement(metaData.file);
3614
- let targetElement: Element;
3615
- if (!isNullOrUndefined(liElement)) {
3616
- targetElement = liElement.querySelector('.' + RESUME_UPLOAD);
3617
- }
3618
- if (!isNullOrUndefined(targetElement) && (isNullOrUndefined(custom) || !custom)) {
3619
- targetElement.classList.remove(RESUME_UPLOAD);
3620
- targetElement.classList.add(PAUSE_UPLOAD);
3621
- targetElement.setAttribute('title', this.localizedTexts('pause'));
3622
- targetElement.nextElementSibling.classList.remove(REMOVE_ICON);
3623
- targetElement.nextElementSibling.classList.add(ABORT_ICON);
3624
- targetElement.nextElementSibling.setAttribute('title', this.localizedTexts('abort'));
3625
- }
3626
- metaData.file.status = this.localizedTexts('inProgress');
3627
- metaData.file.statusCode = '3';
3628
- this.updateMetaData(metaData);
3629
- const eventArgs: PauseResumeEventArgs = {
3630
- event: e ? e : null,
3631
- file: metaData.file,
3632
- chunkIndex: metaData.chunkIndex,
3633
- chunkCount: Math.round(metaData.file.size / this.asyncSettings.chunkSize),
3634
- chunkSize: this.asyncSettings.chunkSize
3635
- };
3636
- this.trigger('resuming', eventArgs);
3637
- for (let i: number = 0; i < this.pausedData.length; i++ ) {
3638
- if (this.pausedData[i as number].end === this.pausedData[i as number].file.size ) {
3639
- this.chunkUploadComplete(e, metaData, custom);
3640
- } else {
3641
- if (this.pausedData[i as number].file.name === metaData.file.name) {
3642
- this.pausedData[i as number].start = this.pausedData[i as number].end;
3643
- this.pausedData[i as number].end = this.pausedData[i as number].end + this.asyncSettings.chunkSize;
3644
- this.pausedData[i as number].end = Math.min(this.pausedData[i as number].end, this.pausedData[i as number].file.size);
3645
- this.pausedData[i as number].chunkIndex = this.pausedData[i as number].chunkIndex + 1;
3646
- this.sendRequest(this.pausedData[i as number].file, this.pausedData[i as number], custom);
3647
- }
3648
- }
3649
- }
3650
- }
3651
-
3652
- private updateMetaData(metaData : MetaData): void {
3653
- if (this.uploadMetaData.indexOf(metaData) === -1) {
3654
- this.uploadMetaData.push(metaData);
3655
- } else {
3656
- this.uploadMetaData.splice(this.uploadMetaData.indexOf(metaData), 1);
3657
- this.uploadMetaData.push(metaData);
3658
- }
3659
- }
3660
-
3661
- private removeChunkProgressBar(metaData: MetaData): void {
3662
- const liElement: HTMLElement = this.getLiElement(metaData.file);
3663
- if (!isNullOrUndefined(liElement)) {
3664
- this.updateProgressBarClasses(liElement, UPLOAD_SUCCESS);
3665
- this.removeProgressbar(liElement, 'success');
3666
- const cancelButton: Element = liElement.querySelector('.' + ABORT_ICON);
3667
- if (!isNullOrUndefined(cancelButton)) {
3668
- cancelButton.classList.add(DELETE_ICON);
3669
- cancelButton.setAttribute('title', this.localizedTexts('delete'));
3670
- cancelButton.classList.remove(ABORT_ICON, UPLOAD_INPROGRESS);
3671
- }
3672
- }
3673
- }
3674
-
3675
- private chunkUploadFailed(e: Event, metaData: MetaData, custom?: boolean): void {
3676
- const chunkCount: number = Math.max(Math.ceil(metaData.file.size / this.asyncSettings.chunkSize), 1);
3677
- let liElement : HTMLElement;
3678
- if (isNullOrUndefined(this.template) && (isNullOrUndefined(custom) || !custom)) {
3679
- liElement = this.getLiElement(metaData.file);
3680
- }
3681
- const requestResponse: Object = e && e.currentTarget ? this.getResponse(e) : null;
3682
- const eventArgs: Object = {
3683
- event: e,
3684
- file: metaData.file,
3685
- chunkIndex: metaData.chunkIndex,
3686
- totalChunk: chunkCount,
3687
- chunkSize: this.asyncSettings.chunkSize,
3688
- cancel: false,
3689
- response: requestResponse
3690
- };
3691
- this.trigger('chunkFailure', eventArgs, (eventArgs: Object) => {
3692
- // To prevent triggering of failure event
3693
- if (!(<any>eventArgs).cancel) {
3694
- if (metaData.retryCount < this.asyncSettings.retryCount) {
3695
- setTimeout(() => {
3696
- this.retryRequest(liElement, metaData, custom);
3697
- }, this.asyncSettings.retryAfterDelay);
3698
- } else {
3699
- if (!isNullOrUndefined(liElement)) {
3700
- const pauseButton: Element = liElement.querySelector('.' + PAUSE_UPLOAD) ?
3701
- liElement.querySelector('.' + PAUSE_UPLOAD) : liElement.querySelector('.' + RESUME_UPLOAD);
3702
- if (!isNullOrUndefined(pauseButton)) {
3703
- pauseButton.classList.add(RETRY_ICON);
3704
- pauseButton.classList.remove(PAUSE_UPLOAD, RESUME_UPLOAD);
3705
- }
3706
- this.updateProgressBarClasses(liElement, UPLOAD_FAILED);
3707
- this.removeProgressbar(liElement, 'failure');
3708
- liElement.querySelector('.e-icons').classList.remove(UPLOAD_INPROGRESS);
3709
- const iconElement: Element = liElement.querySelector('.' + ABORT_ICON) ?
3710
- liElement.querySelector('.' + ABORT_ICON) : liElement.querySelector('.' + REMOVE_ICON);
3711
- iconElement.classList.remove(ABORT_ICON);
3712
- if (!isNullOrUndefined(liElement.querySelector('.' + PAUSE_UPLOAD))) {
3713
- detach(liElement.querySelector('.' + PAUSE_UPLOAD));
3714
- }
3715
- if (metaData.start > 0) {
3716
- iconElement.classList.add(DELETE_ICON);
3717
- iconElement.setAttribute('title', this.localizedTexts('delete'));
3718
- } else {
3719
- iconElement.classList.add(REMOVE_ICON);
3720
- iconElement.setAttribute('title', this.localizedTexts('remove'));
3721
- }
3722
- }
3723
- metaData.retryCount = 0;
3724
- const file: FileInfo = metaData.file;
3725
- const failureMessage: string = this.localizedTexts('uploadFailedMessage');
3726
- const args: Object = {
3727
- e, response: requestResponse,
3728
- operation: 'upload',
3729
- file: this.updateStatus(file, failureMessage, '0', false),
3730
- statusText: failureMessage
3731
- };
3732
- this.trigger('failure', args, (args: Object) => {
3733
- this.updateStatus(file, (args as any).statusText, '0');
3734
- this.uploadSequential();
3735
- this.checkActionComplete(true);
3736
- });
3737
- }
3738
- }
3739
- });
3740
- }
3741
-
3742
- private retryRequest(liElement: HTMLElement, metaData: MetaData, custom?: boolean): void {
3743
- if (isNullOrUndefined(this.template) && (isNullOrUndefined(custom) || !custom) && liElement) {
3744
- this.updateProgressBarClasses(liElement, UPLOAD_FAILED);
3745
- }
3746
- if (metaData) {
3747
- metaData.retryCount += 1;
3748
- this.sendRequest(metaData.file, metaData);
3749
- }
3750
- }
3751
-
3752
- private checkPausePlayAction(e: Event): void {
3753
- const targetElement: HTMLElement = e.target as HTMLElement;
3754
- const selectedElement: HTMLElement = (<HTMLInputElement>e.target).parentElement;
3755
- const index: number = this.fileList.indexOf(selectedElement);
3756
- const fileData: FileInfo = this.filesData[index as number];
3757
- const metaData: MetaData = this.getCurrentMetaData(fileData);
3758
- if (targetElement.classList.contains(PAUSE_UPLOAD)) {
3759
- /* istanbul ignore next */
3760
- this.pauseUpload(metaData, e);
3761
- } else if (targetElement.classList.contains(RESUME_UPLOAD)) {
3762
- /* istanbul ignore next */
3763
- this.resumeUpload(metaData, e);
3764
- } else if (targetElement.classList.contains(RETRY_ICON)) {
3765
- if (metaData.file.status === this.localizedTexts('fileUploadCancel')) {
3766
- this.retryUpload(metaData, false);
3767
- } else {
3768
- this.retryUpload(metaData, true);
3769
- }
3770
- }
3771
- }
3772
-
3773
- private retryUpload(metaData: MetaData, fromcanceledStage: boolean): void {
3774
- if (fromcanceledStage) {
3775
- metaData.end = metaData.end + this.asyncSettings.chunkSize;
3776
- metaData.start = metaData.start + this.asyncSettings.chunkSize;
3777
- this.sendRequest(metaData.file, metaData);
3778
- } else {
3779
- metaData.file.statusCode = '1';
3780
- metaData.file.status = this.localizedTexts('readyToUploadMessage');
3781
- this.chunkUpload(metaData.file);
3782
- }
3783
- /* istanbul ignore next */
3784
- (this.getLiElement(metaData.file)).classList.add(RESTRICT_RETRY);
3785
- }
3786
-
3787
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3788
- private chunkUploadInProgress(e: ProgressEventInit, metaData: MetaData, custom?: boolean): void {
3789
- if (metaData.file.statusCode === '4') {
3790
- return;
3791
- }
3792
- if (metaData.file.statusCode !== '4' && metaData.file.statusCode !== '5') {
3793
- metaData.file.statusCode = '3';
3794
- metaData.file.status = this.localizedTexts('inProgress');
3795
- }
3796
- this.updateMetaData(metaData);
3797
- const liElement: HTMLElement = this.getLiElement(metaData.file);
3798
- if (isNullOrUndefined(liElement)) {
3799
- return;
3800
- }
3801
- const retryElement: Element = liElement.querySelector('.' + RETRY_ICON);
3802
- if (!isNullOrUndefined(retryElement)) {
3803
- retryElement.classList.add(PAUSE_UPLOAD);
3804
- retryElement.setAttribute('title', this.localizedTexts('pause'));
3805
- retryElement.classList.remove(RETRY_ICON);
3806
- }
3807
- if (!isNullOrUndefined(liElement)) {
3808
- if (!(liElement.querySelectorAll('.' + PROGRESS_WRAPPER).length > 0)) {
3809
- const statusElement: Element = liElement.querySelector('.' + STATUS);
3810
- if ( isNullOrUndefined(this.template)) {
3811
- statusElement.classList.add(UPLOAD_INPROGRESS);
3812
- statusElement.classList.remove(UPLOAD_FAILED);
3813
- this.createProgressBar(liElement);
3814
- this.updateProgressBarClasses(liElement, UPLOAD_INPROGRESS);
3815
- }
3816
- const clearIcon: Element = liElement.querySelector('.' + REMOVE_ICON) ? liElement.querySelector('.' + REMOVE_ICON) :
3817
- liElement.querySelector('.' + DELETE_ICON);
3818
- if (!isNullOrUndefined(clearIcon)) {
3819
- clearIcon.classList.add(ABORT_ICON);
3820
- clearIcon.setAttribute('title', this.localizedTexts('abort'));
3821
- clearIcon.classList.remove(REMOVE_ICON);
3822
- }
3823
- }
3824
- if (!isNaN(Math.round((e.loaded / e.total) * 100)) && isNullOrUndefined(this.template) && metaData.file.statusCode !== '4' ) {
3825
- let progressVal: number;
3826
- const totalChunks: number = Math.ceil(metaData.file.size / this.asyncSettings.chunkSize) - 1;
3827
- if (this.asyncSettings.chunkSize && totalChunks) {
3828
- progressVal = Math.round(metaData.chunkIndex / totalChunks * 100);
3829
- this.changeProgressValue(liElement, progressVal.toString() + '%');
3830
- }
3831
- }
3832
- if (metaData.chunkIndex === 0) {
3833
- this.checkActionButtonStatus();
3834
- }
3835
- }
3836
- if (isNullOrUndefined(liElement.querySelector('.' + PAUSE_UPLOAD)) && isNullOrUndefined(this.template)
3837
- && isNullOrUndefined(liElement.querySelector('.' + DELETE_ICON)) ) {
3838
- this.pauseButton = this.createElement('span', {className: 'e-icons e-file-pause-btn', attrs: { 'tabindex': this.btnTabIndex }});
3839
- if (this.browserName === 'msie') {
3840
- this.pauseButton.classList.add('e-msie');
3841
- }
3842
- const abortIcon: Element = liElement.querySelector('.' + ABORT_ICON);
3843
- abortIcon.parentElement.insertBefore(this.pauseButton, abortIcon);
3844
- this.pauseButton.setAttribute('title', this.localizedTexts('pause'));
3845
- this.pauseButton.addEventListener('click', (e: Event) => {
3846
- this.checkPausePlayAction(e);
3847
- }, false);
3848
- }
3849
- }
3850
-
3851
- /**
3852
- * It is used to convert bytes value into kilobytes or megabytes depending on the size based
3853
- * on [binary prefix](https://en.wikipedia.org/wiki/Binary_prefix).
3854
- *
3855
- * @param { number } bytes - Specifies the file size in bytes.
3856
- * @returns {string} - Returns the file size.
3857
- */
3858
- public bytesToSize(bytes : number) : string {
3859
- let i : number = -1;
3860
- if (!bytes) {
3861
- return '0.0 KB';
3862
- }
3863
- do {
3864
- bytes = bytes / 1024;
3865
- i++;
3866
- } while (bytes > 99);
3867
- if (i >= 2) {
3868
- bytes = bytes * 1024;
3869
- i = 1;
3870
- }
3871
- return Math.max(bytes, 0).toFixed(1) + ' ' + ['KB', 'MB'][i as number];
3872
- }
3873
- /**
3874
- * Allows you to sort the file data alphabetically based on its file name clearly.
3875
- *
3876
- * @param { FileList } filesData - specifies the files data for upload.
3877
- * @returns {File[]}
3878
- */
3879
- /* istanbul ignore next */
3880
- public sortFileList(filesData?: FileList): File[] {
3881
- filesData = filesData ? filesData : this.sortFilesList;
3882
- const files: FileList = filesData;
3883
- const fileNames: string[] = [];
3884
- for (let i: number = 0; i < files.length; i++) {
3885
- fileNames.push(files[i as number].name);
3886
- }
3887
- const sortedFileNames: string[] = fileNames.sort();
3888
- const sortedFilesData: File[] = [];
3889
- for (const name of sortedFileNames) {
3890
- for (let i: number = 0; i < files.length; i++) {
3891
- if (name === files[i as number].name) {
3892
- sortedFilesData.push(files[i as number]);
3893
- }
3894
- }
3895
- }
3896
- return sortedFilesData;
3897
- }
3898
-
3899
- /**
3900
- * Removes the component from the DOM and detaches all its related event handlers. Also it removes the attributes and classes.
3901
- *
3902
- * @method destroy
3903
- * @returns {void}
3904
- */
3905
- public destroy(): void {
3906
- this.element.value = null;
3907
- this.clearTemplate();
3908
- this.clearAll();
3909
- this.unWireEvents();
3910
- this.unBindDropEvents();
3911
- if (this.multiple) {
3912
- this.element.removeAttribute('multiple');
3913
- }
3914
- if (!this.enabled) {
3915
- this.element.removeAttribute('disabled');
3916
- }
3917
- this.element.removeAttribute('accept');
3918
- this.setInitialAttributes();
3919
- const attributes: string[] = ['aria-label', 'directory', 'webkitdirectory', 'tabindex'];
3920
- for (const key of attributes) {
3921
- this.element.removeAttribute(key);
3922
- }
3923
- if (!isNullOrUndefined(this.uploadWrapper)) {
3924
- this.uploadWrapper.parentElement.appendChild(this.element);
3925
- detach(this.uploadWrapper);
3926
- }
3927
- this.uploadWrapper = null;
3928
- this.uploadWrapper = null;
3929
- this.browseButton = null;
3930
- this.dropAreaWrapper = null;
3931
- this.dropZoneElement = null;
3932
- this.dropArea = null;
3933
- this.keyboardModule = null;
3934
- this.clearButton = null;
3935
- this.uploadButton = null;
3936
- super.destroy();
3937
- }
3938
-
3939
- /**
3940
- * Allows you to call the upload process manually by calling save URL action.
3941
- * To process the selected files (added in upload queue), pass an empty argument otherwise
3942
- * upload the specific file based on its argument.
3943
- *
3944
- * @param { FileInfo | FileInfo[] } files - Specifies the files data for upload.
3945
- * @param {boolean} custom - Specifies whether the uploader is rendered with custom file list.
3946
- * @returns {void}
3947
- */
3948
- public upload(files?: FileInfo | FileInfo[], custom?: boolean): void {
3949
- files = files ? files : this.filesData;
3950
- if (this.sequentialUpload && (this.isFirstFileOnSelection || custom)) {
3951
- this.sequenceUpload(files as FileInfo[]);
3952
- } else {
3953
- const uploadFiles: FileInfo[] = this.getFilesInArray(files);
3954
- const eventArgs: BeforeUploadEventArgs = {
3955
- customFormData: [],
3956
- currentRequest: null,
3957
- cancel: false
3958
- };
3959
- this.trigger('beforeUpload', eventArgs, (eventArgs: BeforeUploadEventArgs) => {
3960
- if (!eventArgs.cancel) {
3961
- this.customFormDatas = (eventArgs.customFormData && eventArgs.customFormData.length > 0) ?
3962
- eventArgs.customFormData : this.customFormDatas;
3963
- this.uploadFiles(uploadFiles, custom);
3964
- }
3965
- });
3966
- }
3967
- }
3968
- private getFilesInArray(files: FileInfo | FileInfo[]): FileInfo[] {
3969
- let uploadFiles: FileInfo[] = [];
3970
- if (files) {
3971
- if (files instanceof Array) {
3972
- uploadFiles = files;
3973
- } else {
3974
- uploadFiles.push(files);
3975
- }
3976
- }
3977
- return uploadFiles;
3978
- }
3979
-
3980
- /* istanbul ignore next */
3981
- private serverReadFileBase64(fileIndex: number, position: number, totalCount: number) : Promise<string> {
3982
- return new Promise((resolve: Function, reject: Function) => {
3983
- const file: Blob = this.fileStreams[fileIndex as number].rawFile as Blob;
3984
- try {
3985
- const reader: FileReader = new FileReader();
3986
- reader.onload = ((args: any) => {
3987
- return () => {
3988
- try {
3989
- const contents: string = args.result as string;
3990
- const data: string = contents ? contents.split(';base64,')[1] : null;
3991
- resolve(data);
3992
- } catch (e) {
3993
- reject(e);
3994
- }
3995
- };
3996
- })(reader);
3997
- reader.readAsDataURL(file.slice(position, position + totalCount));
3998
- } catch (e) {
3999
- reject(e);
4000
- }
4001
- });
4002
- }
4003
-
4004
- /* eslint-disable @typescript-eslint/no-unused-vars */
4005
- /* istanbul ignore next */
4006
- private uploadFileCount(ele: Element): number {
4007
- /* eslint-enable @typescript-eslint/no-unused-vars */
4008
- const files: FileInfo[] = this.filesData;
4009
- if (!files || files.length === 0) {
4010
- return -1;
4011
- }
4012
- const result: number = files.length;
4013
- return result;
4014
- }
4015
-
4016
- /* eslint-disable @typescript-eslint/no-unused-vars */
4017
- /* istanbul ignore next */
4018
- private getFileRead(index: number, ele: Element): number {
4019
- /* eslint-enable @typescript-eslint/no-unused-vars */
4020
- const files: FileInfo[] = this.filesData;
4021
- if (!files || files.length === 0) {
4022
- return -1;
4023
- }
4024
- const file: FileInfo = files[index as number];
4025
- const fileCount: number = this.newFileRef++;
4026
- this.fileStreams[fileCount as number] = file;
4027
- return fileCount;
4028
- }
4029
-
4030
- private uploadFiles(files: FileInfo[], custom?: boolean): void {
4031
- let selectedFiles: FileInfo[] = [];
4032
- if (this.asyncSettings.saveUrl === '' || isNullOrUndefined(this.asyncSettings.saveUrl)) {
4033
- return;
4034
- }
4035
- if (!custom || isNullOrUndefined(custom)) {
4036
- if (!this.multiple) {
4037
- const file: FileInfo[] = [];
4038
- file.push(files[0]);
4039
- selectedFiles = this.filterfileList(file);
4040
- } else {
4041
- selectedFiles = this.filterfileList(files);
4042
- }
4043
- } else {
4044
- selectedFiles = files;
4045
- }
4046
- for (let i: number = 0; i < selectedFiles.length; i++) {
4047
- this.uploadFilesRequest(selectedFiles, i, custom);
4048
- }
4049
- }
4050
-
4051
- private uploadFilesRequest(selectedFiles: FileInfo[], i: number, custom?: boolean): void {
4052
- const chunkEnabled: boolean = this.checkChunkUpload();
4053
- const ajax: Ajax = new Ajax(this.asyncSettings.saveUrl, 'POST', true, null);
4054
- ajax.emitError = false;
4055
- const eventArgs: UploadingEventArgs = {
4056
- fileData: selectedFiles[i as number],
4057
- customFormData: [],
4058
- cancel: false
4059
- };
4060
- const formData: FormData = new FormData();
4061
- ajax.beforeSend = (e: BeforeSendEventArgs) => {
4062
- eventArgs.currentRequest = ajax.httpRequest;
4063
- this.trigger('uploading', eventArgs, (eventArgs: UploadingEventArgs) => {
4064
- /* istanbul ignore next */
4065
- if (eventArgs.cancel) {
4066
- this.eventCancelByArgs(e, eventArgs, selectedFiles[i as number]);
4067
- }
4068
- if (this.customFormDatas && this.customFormDatas.length > 0) {
4069
- this.updateFormData(formData, this.customFormDatas);
4070
- }
4071
- this.updateFormData(formData, eventArgs.customFormData);
4072
- });
4073
- };
4074
- if (selectedFiles[i as number].statusCode === '1') {
4075
- const name: string = this.element.getAttribute('name');
4076
- formData.append(name, selectedFiles[i as number].rawFile, selectedFiles[i as number].name);
4077
- if (chunkEnabled && selectedFiles[i as number].size > this.asyncSettings.chunkSize) {
4078
- this.chunkUpload(selectedFiles[i as number], custom, i);
4079
- } else {
4080
- ajax.onLoad = (e: Event): object => {
4081
- if (eventArgs.cancel) {
4082
- return {};
4083
- } else {
4084
- this.uploadComplete(e, selectedFiles[i as number], custom);
4085
- return {};
4086
- }
4087
- };
4088
- ajax.onUploadProgress = (e: Event): object => {
4089
- if (eventArgs.cancel) {
4090
- return {};
4091
- } else {
4092
- this.uploadInProgress(e, selectedFiles[i as number], custom, ajax);
4093
- return {};
4094
- }
4095
- };
4096
- /* istanbul ignore next */
4097
- ajax.onError = (e: Event) => {
4098
- this.uploadFailed(e, selectedFiles[i as number]); return {};
4099
- };
4100
- ajax.send(formData);
4101
- }
4102
- }
4103
- }
4104
-
4105
- private spliceFiles(liIndex: number): void {
4106
- const liElement: HTMLElement = this.fileList[liIndex as number];
4107
- const allFiles: FileInfo[] = this.getFilesData();
4108
- const nameElements: number = +liElement.getAttribute('data-files-count');
4109
- let startIndex: number = 0 ;
4110
- for (let i: number = 0; i < liIndex; i++ ) {
4111
- startIndex += (+this.fileList[i as number].getAttribute('data-files-count'));
4112
- }
4113
- const endIndex: number = (startIndex + nameElements) - 1;
4114
- for (let j: number = endIndex; j >= startIndex; j--) {
4115
- allFiles.splice(j, 1);
4116
- }
4117
- }
4118
- /* eslint-disable valid-jsdoc, jsdoc/require-param */
4119
- /**
4120
- * Remove the uploaded file from server manually by calling the remove URL action.
4121
- * If you pass an empty argument to this method, the complete file list can be cleared,
4122
- * otherwise remove the specific file based on its argument (“file_data”).
4123
- *
4124
- * @param { FileInfo | FileInfo[] } fileData - specifies the files data to remove from file list/server.
4125
- * @param { boolean } customTemplate - Set true if the component rendering with customize template.
4126
- * @param { boolean } removeDirectly - Set true if files remove without removing event.
4127
- * @param { boolean } postRawFile - Set false, to post file name only to the remove action.
4128
- * @returns {void}
4129
- */
4130
- public remove(
4131
- fileData?: FileInfo | FileInfo[], customTemplate?: boolean, removeDirectly?: boolean,
4132
- postRawFile?: boolean, args?: MouseEvent | TouchEvent | KeyboardEventArgs): void {
4133
- if (isNullOrUndefined(postRawFile)) {
4134
- postRawFile = true;
4135
- }
4136
- const eventArgs: RemovingEventArgs = {
4137
- event: args,
4138
- cancel: false,
4139
- filesData: [],
4140
- customFormData: [],
4141
- postRawFile: postRawFile,
4142
- currentRequest: null
4143
- };
4144
- const beforeEventArgs: BeforeRemoveEventArgs = {
4145
- cancel: false,
4146
- customFormData: [],
4147
- currentRequest: null
4148
- };
4149
- this.trigger('beforeRemove', beforeEventArgs, (beforeEventArgs: BeforeRemoveEventArgs) => {
4150
- if (!beforeEventArgs.cancel) {
4151
- if (this.isFormUpload()) {
4152
- eventArgs.filesData = fileData as FileInfo[];
4153
- this.trigger('removing', eventArgs, (eventArgs: RemovingEventArgs) => {
4154
- if (!eventArgs.cancel) {
4155
- const removingFiles: FileInfo[] = this.getFilesInArray(fileData);
4156
- let isLiRemoved: boolean = false;
4157
- let liIndex: number;
4158
- for (const data of removingFiles) {
4159
- if (!isLiRemoved) {
4160
- liIndex = this.fileList.indexOf(data.list);
4161
- }
4162
- if (liIndex > -1) {
4163
- const inputElement: HTMLElement = !isNullOrUndefined(data.input) ? data.input : null;
4164
- if (inputElement) {
4165
- detach(inputElement);
4166
- }
4167
- this.spliceFiles(liIndex);
4168
- detach(this.fileList[liIndex as number]);
4169
- this.fileList.splice(liIndex, 1);
4170
- isLiRemoved = true;
4171
- liIndex = -1;
4172
- }
4173
- }
4174
- }
4175
- });
4176
- } else if (this.isForm && (isNullOrUndefined(this.asyncSettings.removeUrl) || this.asyncSettings.removeUrl === '')) {
4177
- eventArgs.filesData = this.getFilesData();
4178
- this.trigger('removing', eventArgs, (eventArgs: RemovingEventArgs) => {
4179
- if (!eventArgs.cancel) {
4180
- this.clearAll();
4181
- }
4182
- });
4183
- } else {
4184
- let removeFiles: FileInfo[] = [];
4185
- fileData = !isNullOrUndefined(fileData) ? fileData : this.filesData;
4186
- if (fileData instanceof Array) {
4187
- removeFiles = fileData;
4188
- } else {
4189
- removeFiles.push(fileData);
4190
- }
4191
- eventArgs.filesData = removeFiles;
4192
- const removeUrl: string = this.asyncSettings.removeUrl;
4193
- const validUrl: boolean = (removeUrl === '' || isNullOrUndefined(removeUrl)) ? false : true;
4194
- for (const files of removeFiles) {
4195
- const fileUploadedIndex : number = this.uploadedFilesData.indexOf(files);
4196
- if ((files.statusCode === '2' || files.statusCode === '4' || (files.statusCode === '0' &&
4197
- fileUploadedIndex !== -1)) && validUrl) {
4198
- this.removeUploadedFile(files, eventArgs, removeDirectly, customTemplate);
4199
- } else {
4200
- if (!removeDirectly) {
4201
- this.trigger('removing', eventArgs, (eventArgs: RemovingEventArgs) => {
4202
- if (!eventArgs.cancel) {
4203
- this.removeFilesData(files, customTemplate);
4204
- }
4205
- });
4206
- } else {
4207
- this.removeFilesData(files, customTemplate);
4208
- }
4209
- }
4210
- if (args && !(args.target as Element).classList.contains(REMOVE_ICON)) {
4211
- this.checkActionComplete(false);
4212
- }
4213
- }
4214
- }
4215
- }
4216
- });
4217
- }
4218
- /* eslint-enable valid-jsdoc, jsdoc/require-param */
4219
- /**
4220
- * Clear all the file entries from list that can be uploaded files or added in upload queue.
4221
- *
4222
- * @returns {void}
4223
- */
4224
- public clearAll(): void {
4225
- if (isNullOrUndefined(this.listParent)) {
4226
- if (this.browserName !== 'msie') {
4227
- this.element.value = '';
4228
- }
4229
- this.filesData = [];
4230
- return;
4231
- }
4232
- const eventArgs: ClearingEventArgs = {
4233
- cancel: false,
4234
- filesData: this.filesData
4235
- };
4236
- this.trigger('clearing', eventArgs, (eventArgs: ClearingEventArgs) => {
4237
- if (!eventArgs.cancel) {
4238
- this.clearData();
4239
- this.actionCompleteCount = 0;
4240
- this.count = -1;
4241
- }
4242
- });
4243
- }
4244
- /* eslint-disable valid-jsdoc, jsdoc/require-returns-description */
4245
- /**
4246
- * Get the data of files which are shown in file list.
4247
- *
4248
- * @param { number } index - specifies the file list item(li) index.
4249
- * @returns {FileInfo[]}
4250
- */
4251
- public getFilesData(index?: number): FileInfo[] {
4252
- if (isNullOrUndefined(index)) {
4253
- return this.filesData;
4254
- } else {
4255
- return this.getSelectedFiles(index);
4256
- }
4257
- }
4258
- /* eslint-enable valid-jsdoc, jsdoc/require-returns-description */
4259
- /**
4260
- * Pauses the in-progress chunked upload based on the file data.
4261
- *
4262
- * @param { FileInfo | FileInfo[] } fileData - specifies the files data to pause from uploading.
4263
- * @param { boolean } custom - Set true if used custom UI.
4264
- * @returns {void}
4265
- */
4266
- public pause(fileData?: FileInfo | FileInfo[], custom?: boolean): void {
4267
- fileData = fileData ? fileData : this.filesData;
4268
- const fileDataFiles: FileInfo[] = this.getFilesInArray(fileData);
4269
- this.pauseUploading(fileDataFiles, custom);
4270
- }
4271
- private pauseUploading(fileData: FileInfo[], custom?: boolean): void {
4272
- const files: FileInfo[] = this.getFiles(fileData);
4273
- for (let i: number = 0; i < files.length; i++) {
4274
- if (files[i as number].statusCode === '3') {
4275
- this.pauseUpload(this.getCurrentMetaData(files[i as number], null), null, custom);
4276
- }
4277
- }
4278
- }
4279
-
4280
- private getFiles (fileData: FileInfo[]) : FileInfo[] {
4281
- let files: FileInfo[] = [];
4282
- if (!isNullOrUndefined(fileData) && !(fileData instanceof Array)) {
4283
- files.push(fileData);
4284
- } else {
4285
- files = fileData;
4286
- }
4287
- return files;
4288
- }
4289
-
4290
- /**
4291
- * Resumes the chunked upload that is previously paused based on the file data.
4292
- *
4293
- * @param { FileInfo | FileInfo[] } fileData - specifies the files data to resume the paused file.
4294
- * @param { boolean } custom - Set true if used custom UI.
4295
- * @returns {void}
4296
- */
4297
- public resume(fileData?: FileInfo | FileInfo[], custom?: boolean): void {
4298
- fileData = fileData ? fileData : this.filesData;
4299
- const fileDataFiles: FileInfo[] = this.getFilesInArray(fileData);
4300
- this.resumeFiles(fileDataFiles, custom);
4301
- }
4302
-
4303
- private resumeFiles(fileData: FileInfo[], custom?: boolean): void {
4304
- const files: FileInfo[] = this.getFiles(fileData);
4305
- for (let i: number = 0; i < files.length; i++) {
4306
- if (files[i as number].statusCode === '4') {
4307
- this.resumeUpload(this.getCurrentMetaData(files[i as number], null), null, custom);
4308
- }
4309
- }
4310
- }
4311
-
4312
- /**
4313
- * Retries the canceled or failed file upload based on the file data.
4314
- *
4315
- * @param { FileInfo | FileInfo[] } fileData - specifies the files data to retry the canceled or failed file.
4316
- * @param { boolean } fromcanceledStage - Set true to retry from canceled stage and set false to retry from initial stage.
4317
- * @param {boolean} custom -Specifies whether the uploader is rendered with custom file list.
4318
- * @returns {void}
4319
- */
4320
- public retry(fileData?: FileInfo | FileInfo[], fromcanceledStage?: boolean, custom?: boolean): void {
4321
- fileData = fileData ? fileData : this.filesData;
4322
- const fileDataFiles: FileInfo[] = this.getFilesInArray(fileData);
4323
- if (this.sequentialUpload && this.isFirstFileOnSelection) {
4324
- this.isFirstFileOnSelection = false;
4325
- }
4326
- this.retryFailedFiles(fileDataFiles, fromcanceledStage, custom);
4327
- }
4328
-
4329
- private retryFailedFiles(fileData: FileInfo[], fromcanceledStage: boolean, custom: boolean): void {
4330
- const files: FileInfo[] = this.getFiles(fileData);
4331
- for (let i: number = 0; i < files.length; i++) {
4332
- if (files[i as number].statusCode === '5' || files[i as number].statusCode === '0') {
4333
- if (this.asyncSettings.chunkSize > 0 && this.getCurrentMetaData(files[i as number], null)) {
4334
- this.retryUpload(this.getCurrentMetaData(files[i as number], null), fromcanceledStage);
4335
- } else {
4336
- let liElement: HTMLElement;
4337
- if (!custom) {
4338
- liElement = this.fileList[this.filesData.indexOf(files[i as number])];
4339
- }
4340
- this.reloadcanceledFile(null, files[i as number], liElement, custom);
4341
- }
4342
- }
4343
- }
4344
- }
4345
-
4346
- /**
4347
- * Stops the in-progress chunked upload based on the file data.
4348
- * When the file upload is canceled, the partially uploaded file is removed from server.
4349
- *
4350
- * @param { FileInfo | FileInfo[] } fileData - specifies the files data to cancel the progressing file.
4351
- * @returns {void}
4352
- */
4353
- public cancel(fileData?: FileInfo[]): void {
4354
- fileData = fileData ? fileData : this.filesData;
4355
- const cancelingFiles: FileInfo[] = this.getFilesInArray(fileData);
4356
- this.cancelUpload(cancelingFiles);
4357
- }
4358
-
4359
- private cancelUpload(fileData: FileInfo[]): void {
4360
- const files: FileInfo[] = this.getFiles(fileData);
4361
- if (this.asyncSettings.chunkSize > 0) {
4362
- for (let i: number = 0; i < files.length; i++) {
4363
- if (files[i as number].statusCode === '3') {
4364
- const metaData: MetaData = this.getCurrentMetaData(files[i as number], null);
4365
- metaData.file.statusCode = '5';
4366
- metaData.file.status = this.localizedTexts('fileUploadCancel');
4367
- this.updateMetaData(metaData);
4368
- this.showHideUploadSpinner(files[i as number]);
4369
- }
4370
- }
4371
- } else {
4372
- for (let i: number = 0; i < files.length; i++) {
4373
- if (files[i as number].statusCode === '3') {
4374
- files[i as number].statusCode = '5';
4375
- files[i as number].status = this.localizedTexts('fileUploadCancel');
4376
- this.showHideUploadSpinner(files[i as number]);
4377
- }
4378
- }
4379
- }
4380
- }
4381
-
4382
- private showHideUploadSpinner(files: FileInfo): void {
4383
- const liElement: HTMLElement = this.getLiElement(files);
4384
- if (!isNullOrUndefined(liElement) && isNullOrUndefined(this.template)) {
4385
- const spinnerTarget: HTMLElement = liElement.querySelector('.' + ABORT_ICON) as HTMLElement;
4386
- createSpinner({ target: spinnerTarget , width: '20px' });
4387
- showSpinner(spinnerTarget);
4388
- }
4389
- }
4390
- }
4391
- /* eslint-enable @typescript-eslint/no-explicit-any */