quasar-ui-danx 0.0.10 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. package/package.json +8 -2
  2. package/src/components/ActionTable/ActionTable.vue +143 -0
  3. package/src/components/ActionTable/BatchActionMenu.vue +60 -0
  4. package/src/components/ActionTable/EmptyTableState.vue +33 -0
  5. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +36 -0
  6. package/src/components/ActionTable/Filters/FilterGroupItem.vue +28 -0
  7. package/src/components/ActionTable/Filters/FilterGroupList.vue +76 -0
  8. package/src/components/ActionTable/Filters/FilterListToggle.vue +50 -0
  9. package/src/components/ActionTable/Filters/FilterableField.vue +143 -0
  10. package/src/components/ActionTable/Filters/index.ts +5 -0
  11. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  12. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  13. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  14. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  16. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  17. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  18. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  19. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  20. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  21. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  22. package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
  23. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  24. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  25. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  26. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  27. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  28. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  29. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  30. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  31. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  32. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  33. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  34. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  35. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  36. package/src/components/ActionTable/Form/RenderedForm.vue +76 -0
  37. package/src/components/ActionTable/Form/index.ts +2 -0
  38. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  39. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  40. package/src/components/ActionTable/index.ts +10 -0
  41. package/src/components/ActionTable/listActions.ts +362 -0
  42. package/src/components/ActionTable/listHelpers.ts +74 -0
  43. package/src/components/ActionTable/tableColumns.ts +72 -0
  44. package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
  45. package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
  46. package/src/components/DragAndDrop/index.ts +0 -1
  47. package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
  48. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  49. package/src/components/Utility/ContentDrawer.vue +70 -0
  50. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  51. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  52. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
  53. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  54. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  55. package/src/components/Utility/ImagePreview.vue +192 -0
  56. package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
  57. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  58. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  59. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  60. package/src/components/Utility/index.ts +11 -0
  61. package/src/components/index.ts +3 -0
  62. package/src/helpers/FileUpload.ts +295 -0
  63. package/src/helpers/FlashMessages.ts +79 -0
  64. package/src/helpers/array.ts +37 -0
  65. package/src/helpers/compatibility.ts +64 -0
  66. package/src/helpers/date.ts +5 -0
  67. package/src/helpers/download.ts +200 -0
  68. package/src/helpers/downloadPdf.ts +92 -0
  69. package/src/helpers/files.ts +52 -0
  70. package/src/helpers/formats.ts +183 -0
  71. package/src/helpers/http.ts +62 -0
  72. package/src/helpers/index.ts +12 -1
  73. package/src/helpers/multiFileUpload.ts +68 -0
  74. package/src/helpers/singleFileUpload.ts +54 -0
  75. package/src/helpers/storage.ts +8 -0
  76. package/src/index.esm.js +3 -4
  77. package/src/svg/FilterIcon.svg +7 -0
  78. package/src/svg/ImageIcon.svg +30 -0
  79. package/src/svg/PdfIcon.svg +21 -0
  80. package/src/svg/PercentIcon.svg +13 -0
  81. package/src/svg/TrashIcon.svg +15 -0
  82. package/src/svg/XIcon.svg +18 -0
  83. package/src/svg/index.ts +8 -0
  84. package/src/vendor/tinymce-config.ts +1 -0
  85. package/src/vue-plugin.js +7 -4
  86. package/tsconfig.json +14 -13
  87. package/src/components/DragAndDrop/Icons/index.ts +0 -2
  88. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
  89. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -0,0 +1,295 @@
1
+ import { FlashMessages, resolveFileLocation } from "@ui/helpers";
2
+ import { uid } from "quasar";
3
+
4
+ export type FileUploadOptions = {
5
+ directory: string,
6
+ presignedUploadUrl: (...params) => "",
7
+ uploadCompletedUrl: (...params) => "",
8
+ };
9
+
10
+ export class FileUpload {
11
+ files: { id: string, blobUrl: string }[] = [];
12
+ fileUploads = [];
13
+ onErrorCb = null;
14
+ onProgressCb = null;
15
+ onCompleteCb = null;
16
+ onAllCompleteCb = null;
17
+ options: FileUploadOptions = {
18
+ directory: "file-upload",
19
+ presignedUploadUrl: null,
20
+ uploadCompletedUrl: null
21
+ };
22
+
23
+ constructor(files, options: FileUploadOptions) {
24
+ if (!Array.isArray(files) && !(files instanceof FileList)) {
25
+ files = [files];
26
+ }
27
+ this.files = files;
28
+ this.fileUploads = [];
29
+ this.onErrorCb = null;
30
+ this.onProgressCb = null;
31
+ this.onCompleteCb = null;
32
+ this.onAllCompleteCb = null;
33
+
34
+ this.options = {
35
+ ...this.options,
36
+ ...options
37
+ };
38
+ this.prepare();
39
+ }
40
+
41
+ /**
42
+ * Prepares all files for upload and provides an id and blobUrl for each file
43
+ */
44
+ prepare() {
45
+ // Prepare required attributes
46
+ for (const file of this.files) {
47
+ if (!(file instanceof File)) {
48
+ throw Error(
49
+ "FileUpload constructor requires a File object or a list of File objects"
50
+ );
51
+ }
52
+
53
+ file.id = uid();
54
+ file.blobUrl = URL.createObjectURL(file);
55
+
56
+ // Prepare FormData
57
+ const formData = new FormData();
58
+ formData.append("file", file);
59
+
60
+ this.fileUploads.push({
61
+ file,
62
+ xhr: null, // NOTE: The XHR will be setup asynchronously right before sending file uploads
63
+ formData,
64
+ isComplete: false
65
+ });
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Callback for when all files have been uploaded
71
+ */
72
+ onAllComplete(cb) {
73
+ this.onAllCompleteCb = cb;
74
+ return this;
75
+ }
76
+
77
+ /**
78
+ * Callback fired once for each file upon successful completion of upload
79
+ * @param cb
80
+ * @returns {FileUpload}
81
+ */
82
+ onComplete(cb) {
83
+ this.onCompleteCb = cb;
84
+ return this;
85
+ }
86
+
87
+ /**
88
+ * Callback fired each time there is an upload progress update for a file
89
+ * @param cb
90
+ * @returns {FileUpload}
91
+ */
92
+ onProgress(cb) {
93
+ this.onProgressCb = cb;
94
+ return this;
95
+ }
96
+
97
+ /**
98
+ * Callback fired when an error occurs during upload
99
+ * @param cb
100
+ * @returns {FileUpload}
101
+ */
102
+ onError(cb) {
103
+ this.onErrorCb = cb;
104
+ return this;
105
+ }
106
+
107
+ /**
108
+ * Handles the error events / fires the callback if it is set
109
+ * @param e
110
+ * @param file
111
+ * @param error
112
+ */
113
+ errorHandler(e, file, error = null) {
114
+ if (this.onErrorCb) {
115
+ this.onErrorCb({ e, file, error });
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Resolve the locations of all the files
121
+ * @returns {Promise<FileUpload>}
122
+ */
123
+ async resolveLocation(waitMessage = null) {
124
+ for (const fileUpload of this.fileUploads) {
125
+ fileUpload.file.location = await resolveFileLocation(
126
+ fileUpload.file,
127
+ waitMessage
128
+ );
129
+ fileUpload.formData.append(
130
+ "meta",
131
+ JSON.stringify(fileUpload.file.location)
132
+ );
133
+ }
134
+ return this;
135
+ }
136
+
137
+ /**
138
+ * Fires the progress callback
139
+ * @param fileUpload
140
+ * @param progress
141
+ */
142
+ fireProgressCallback(fileUpload, progress) {
143
+ fileUpload.file.progress = progress;
144
+ this.onProgressCb && this.onProgressCb({ file: this.wrapFile(fileUpload.file), progress });
145
+ }
146
+
147
+ /**
148
+ * Fires the complete callback
149
+ * @param fileUpload
150
+ * @param uploadedFile
151
+ */
152
+ fireCompleteCallback(fileUpload, uploadedFile) {
153
+ fileUpload.isComplete = true;
154
+ fileUpload.file.progress = 1;
155
+ this.onCompleteCb && this.onCompleteCb({ file: this.wrapFile(fileUpload.file), uploadedFile });
156
+ }
157
+
158
+ /**
159
+ * Check if all files have been uploaded and call the callback if they have
160
+ */
161
+ checkAllComplete() {
162
+ if (this.onAllCompleteCb) {
163
+ if (this.fileUploads.every((fileUpload) => fileUpload.isComplete)) {
164
+ this.onAllCompleteCb({ files: this.fileUploads });
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Returns a native JS object that is easier to work with than the File objects (no weird behavior of missing
171
+ * properties, easily printable, etc.)
172
+ * @param file
173
+ * @returns {{size, name, progress, location, blobUrl: *, id, type}}
174
+ */
175
+ wrapFile(file) {
176
+ return {
177
+ id: file.id,
178
+ name: file.name,
179
+ size: file.size,
180
+ type: file.type,
181
+ progress: file.progress,
182
+ location: file.location,
183
+ blobUrl: file.blobUrl
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Registers all the callbacks requested for the XHR / post-processing of file uploads
189
+ */
190
+ setXhrCallbacks() {
191
+ // Set the error callbacks
192
+ for (const fileUpload of this.fileUploads) {
193
+ fileUpload.xhr.addEventListener(
194
+ "error",
195
+ (e) => this.errorHandler(e, fileUpload.file),
196
+ false
197
+ );
198
+ }
199
+
200
+ // Set the progress callbacks
201
+ if (this.onProgressCb) {
202
+ for (const fileUpload of this.fileUploads) {
203
+ fileUpload.xhr.upload.addEventListener(
204
+ "progress",
205
+ (e) => {
206
+ // Max of 95%, so we can indicate we are completing the signed URL process
207
+ const progress = Math.min(.95, e.loaded / e.total);
208
+ this.fireProgressCallback(fileUpload, progress);
209
+ },
210
+ false
211
+ );
212
+ }
213
+ }
214
+
215
+ // Set the load callbacks which registers the Complete / All Complete callbacks and handles non-xhr related
216
+ // errors
217
+ for (const fileUpload of this.fileUploads) {
218
+ fileUpload.xhr.addEventListener(
219
+ "load",
220
+ async (e) => {
221
+ try {
222
+ // First complete the presigned upload to get the updated file resource data
223
+ const uploadedFile = await this.completePresignedUpload(fileUpload);
224
+
225
+ // Fire the file complete callbacks
226
+ this.fireCompleteCallback(fileUpload, uploadedFile);
227
+ this.checkAllComplete();
228
+ } catch (error) {
229
+ this.errorHandler(e, fileUpload.file, error);
230
+ }
231
+ },
232
+ false
233
+ );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Mark the presigned upload as completed and return the file resource from the platform server
239
+ * @param fileUpload
240
+ * @returns {Promise<void>}
241
+ */
242
+ async completePresignedUpload(fileUpload) {
243
+ // Show 95% as the last 5% will be to complete the presigned upload
244
+ this.fireProgressCallback(fileUpload, .95);
245
+
246
+ // Let the platform know the presigned upload is complete
247
+ return await fetch(this.options.uploadCompletedUrl(fileUpload.file.resource_id), { method: "POST" }).then(r => r.json());
248
+ }
249
+
250
+ /**
251
+ * Start uploading all files
252
+ */
253
+ async upload() {
254
+ for (const fileUpload of this.fileUploads) {
255
+ const mimeType = fileUpload.file.mimeType || fileUpload.file.type;
256
+ const presignedUrl = this.options.presignedUploadUrl(this.options.directory, fileUpload.file.name, mimeType);
257
+
258
+ // Fetch presigned upload URL
259
+ const fileResource = await fetch(presignedUrl).then(r => r.json());
260
+
261
+ if (!fileResource.url) {
262
+ FlashMessages.error("Could not fetch presigned upload URL for file " + fileUpload.file.name);
263
+ continue;
264
+ }
265
+
266
+ const isS3Upload = !fileResource.url.match("upload-presigned-url-contents");
267
+
268
+ // We need the file resource ID to complete the presigned upload
269
+ fileUpload.file.resource_id = fileResource.id;
270
+
271
+ // Prepare XHR request
272
+ const xhr = new XMLHttpRequest();
273
+
274
+ // The XHR request is different based on weather we're sending to S3 or the platform server
275
+ if (isS3Upload) {
276
+ xhr.open("PUT", fileResource.url);
277
+ xhr.setRequestHeader("Content-Type", mimeType);
278
+ fileUpload.body = fileUpload.file;
279
+ } else {
280
+ xhr.open("POST", fileResource.url);
281
+ fileUpload.body = fileUpload.formData;
282
+ }
283
+
284
+ fileUpload.xhr = xhr;
285
+ }
286
+
287
+ // Set all the callbacks on the XHR requests
288
+ this.setXhrCallbacks();
289
+
290
+ // Send all the XHR file uploads
291
+ for (const fileUpload of this.fileUploads) {
292
+ fileUpload.xhr.send(fileUpload.body);
293
+ }
294
+ }
295
+ }
@@ -0,0 +1,79 @@
1
+ import { watch } from "vue";
2
+
3
+ export class FlashMessages {
4
+ static notify;
5
+
6
+ static PROP_DEFINITIONS = {
7
+ successMsg: {
8
+ type: String,
9
+ default: ""
10
+ },
11
+ errorMsg: {
12
+ type: String,
13
+ default: ""
14
+ },
15
+ warningMsg: {
16
+ type: String,
17
+ default: ""
18
+ }
19
+ };
20
+
21
+ static enable(msgProps) {
22
+ FlashMessages.success(msgProps.successMsg);
23
+ FlashMessages.error(msgProps.errorMsg);
24
+ FlashMessages.warning(msgProps.warningMsg);
25
+ watch(() => msgProps.successMsg, FlashMessages.success);
26
+ watch(() => msgProps.errorMsg, FlashMessages.error);
27
+ watch(() => msgProps.warningMsg, FlashMessages.warning);
28
+ }
29
+
30
+ static send(message, options = {}) {
31
+ if (message) {
32
+ FlashMessages.notify({
33
+ message,
34
+ timeout: 2500,
35
+ color: "gray-base",
36
+ textColor: "white",
37
+ position: "top",
38
+ closeBtn: "X",
39
+ ...options
40
+ });
41
+ }
42
+ }
43
+
44
+ static success(message, options = {}) {
45
+ FlashMessages.send(message, {
46
+ color: "green-light",
47
+ textColor: "green-dark",
48
+ icon: "hero:check-circle",
49
+ ...options
50
+ });
51
+ }
52
+
53
+ static error(message, options = {}) {
54
+ FlashMessages.send(message, {
55
+ color: "red-light",
56
+ textColor: "red-dark",
57
+ icon: "hero:alert",
58
+ ...options
59
+ });
60
+ }
61
+
62
+ static warning(message, options = {}) {
63
+ FlashMessages.send(message, {
64
+ color: "yellow-lighter",
65
+ textColor: "yellow-base",
66
+ icon: "hero:alert",
67
+ ...options
68
+ });
69
+ }
70
+
71
+ static combine(type, messages, options = {}) {
72
+ FlashMessages[type](messages.map(m => typeof m === "string" ? m : (m.message || m.Message)).join("<br/>"), {
73
+ ...options,
74
+ html: true
75
+ });
76
+ }
77
+ }
78
+
79
+ export const notify = new FlashMessages();
@@ -0,0 +1,37 @@
1
+ /**
2
+ *
3
+ * @param array
4
+ * @param item
5
+ * @param newItem
6
+ * @returns {*[]}
7
+ */
8
+ export function replace(array, item, newItem = undefined) {
9
+ const index =
10
+ typeof item === "function" ? array.findIndex(item) : array.indexOf(item);
11
+ if (index === false) {
12
+ console.error("Item not found in array", item, array);
13
+ throw new Error("Item not found in array");
14
+ }
15
+ const newArray = [...array];
16
+ newItem !== undefined
17
+ ? newArray.splice(index, 1, newItem)
18
+ : newArray.splice(index, 1);
19
+ return newArray;
20
+ }
21
+
22
+ export function remove(array, item) {
23
+ return replace(array, item);
24
+ }
25
+
26
+ /**
27
+ * Remove duplicate items from an array using a callback to compare 2 elements
28
+ * @param array
29
+ * @param cb
30
+ * @returns {*}
31
+ */
32
+ export function uniqueBy(array, cb) {
33
+ return array.filter((a, index, self) => {
34
+ // Check if the current element 'a' is the first occurrence in the array
35
+ return index === self.findIndex((b) => cb(a, b));
36
+ });
37
+ }
@@ -0,0 +1,64 @@
1
+ import { sleep } from "@ui/helpers";
2
+ import { useGeolocation } from "@vueuse/core";
3
+ import { computed } from "vue";
4
+
5
+ let isLoaded = false;
6
+ let hasAlreadyWaited = false;
7
+ let geolocationError = null;
8
+ let hasLocation = null;
9
+ let geolocation = null;
10
+
11
+ export function useCompatibility(requestLocation = true) {
12
+ if (!isLoaded && requestLocation) {
13
+ const { coords, error, locatedAt } = useGeolocation();
14
+ geolocationError = error;
15
+ hasLocation = locatedAt;
16
+ geolocation = coords;
17
+ isLoaded = true;
18
+ }
19
+
20
+ const isLocationSupported = "geolocation" in navigator;
21
+
22
+ const location = computed(() => {
23
+ if (hasLocation?.value) {
24
+ return geolocation?.value;
25
+ }
26
+ return null;
27
+ });
28
+
29
+ const isCompatible = computed(() => !geolocationError?.value && !!hasLocation?.value);
30
+
31
+ /**
32
+ * Wait for location to be available and returns the location when it is or null after the wait period times out.
33
+ * @param maxWait
34
+ */
35
+ const waitForLocation = async (maxWait = 3000) => {
36
+ // We only should wait once, if we already waited and failed, its unlikely the location will be available at a
37
+ // later time
38
+ if (hasAlreadyWaited) {
39
+ return location;
40
+ }
41
+
42
+ hasAlreadyWaited = true;
43
+ let waitTime = 0;
44
+ while (!location.value) {
45
+ await sleep(100);
46
+ waitTime += 100;
47
+
48
+ if (waitTime > maxWait) {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ return location;
54
+ };
55
+
56
+ return {
57
+ isLocationSupported,
58
+ isCompatible,
59
+ geolocationError,
60
+ hasLocation,
61
+ location,
62
+ waitForLocation
63
+ };
64
+ }
@@ -0,0 +1,5 @@
1
+ import { parseDateTime } from "@ui/helpers/formats";
2
+
3
+ export function diffInDays(date1, date2) {
4
+ return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
5
+ }
@@ -0,0 +1,200 @@
1
+ // download.js v4.2, by dandavis; 2008-2016. [CCBY2] see http://danml.com/download.html for tests/usage
2
+ // v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and
3
+ // optional mime v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for
4
+ // larger+faster saves than dataURLs v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback
5
+ // was improved with force-download mime and base64 support. 3.1 improved safari handling. v4 adds AMD/UMD, commonJS,
6
+ // and plain browser support v4.1 adds url download capability via solo URL argument (same domain/CORS only) v4.2 adds
7
+ // semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
8
+ // https://github.com/rndme/download
9
+
10
+ /* eslint-disable */
11
+ export function download(data, strFileName, strMimeType) {
12
+ var self = window;
13
+ // this script is only for browsers anyway...
14
+
15
+ var defaultMime = "application/octet-stream";
16
+ // this default mime also triggers iframe downloads
17
+
18
+ var mimeType = strMimeType || defaultMime;
19
+
20
+ var payload = data;
21
+
22
+ var url = !strFileName && !strMimeType && payload;
23
+
24
+ var anchor = document.createElement("a");
25
+
26
+ var toString = function (a) {
27
+ return String(a);
28
+ };
29
+
30
+ // @ts-ignore
31
+ var myBlob = self.Blob || self.MozBlob || self.WebKitBlob || toString;
32
+
33
+ var fileName = strFileName || "download";
34
+
35
+ var blob;
36
+
37
+ var reader;
38
+ myBlob = myBlob.call ? myBlob.bind(self) : Blob;
39
+
40
+ if (String(this) === "true") {
41
+ // reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
42
+ payload = [payload, mimeType];
43
+ mimeType = payload[0];
44
+ payload = payload[1];
45
+ }
46
+
47
+ if (url && url.length < 2048) {
48
+ // if no filename and no mime, assume a url was passed as the only argument
49
+ fileName = url.split("/").pop().split("?")[0];
50
+ anchor.href = url; // assign href prop to temp anchor
51
+
52
+ // if the browser determines that it's a potentially valid url path:
53
+ if (
54
+ anchor.href.indexOf(url) !== -1 ||
55
+ anchor.href.indexOf(encodeURI(url)) !== -1 ||
56
+ anchor.href === encodeURI(url)
57
+ ) {
58
+ var ajax = new XMLHttpRequest();
59
+ ajax.open("GET", url + "?no-cache=" + Date.now(), true);
60
+ ajax.responseType = "blob";
61
+ ajax.onload = function (e) {
62
+ // @ts-ignore
63
+ download(e.target.response, fileName, defaultMime);
64
+ };
65
+ ajax.onerror = function (e) {
66
+ // As a fallback, just open the request in a new tab
67
+ window.open(url, "_blank").focus();
68
+ };
69
+ setTimeout(function () {
70
+ ajax.send();
71
+ }, 0); // allows setting custom ajax headers using the return:
72
+ return ajax;
73
+ } else {
74
+ throw new Error("Invalid URL given, cannot download file: " + url);
75
+ }
76
+ } // end if url?
77
+
78
+ // go ahead and download dataURLs right away
79
+ if (/^data:[\w+-]+\/[\w+-]+[,;]/.test(payload)) {
80
+ if (payload.length > 1024 * 1024 * 1.999 && myBlob !== toString) {
81
+ payload = dataUrlToBlob(payload);
82
+ mimeType = payload.type || defaultMime;
83
+ } else {
84
+ // IE10 can't do a[download], only Blobs
85
+ // everyone else can save dataURLs un-processed
86
+ // @ts-ignore
87
+ return navigator.msSaveBlob ? navigator.msSaveBlob(dataUrlToBlob(payload), fileName) : saver(payload);
88
+ }
89
+ } // end if dataURL passed?
90
+
91
+ blob =
92
+ payload instanceof myBlob
93
+ ? payload
94
+ : new myBlob([payload], { type: mimeType });
95
+
96
+ function dataUrlToBlob(strUrl) {
97
+ var parts = strUrl.split(/[:;,]/);
98
+
99
+ var type = parts[1];
100
+
101
+ var decoder = parts[2] === "base64" ? atob : decodeURIComponent;
102
+
103
+ var binData = decoder(parts.pop());
104
+
105
+ var mx = binData.length;
106
+
107
+ var i = 0;
108
+
109
+ var uiArr = new Uint8Array(mx);
110
+
111
+ for (i; i < mx; ++i) uiArr[i] = binData.charCodeAt(i);
112
+
113
+ return new myBlob([uiArr], { type: type });
114
+ }
115
+
116
+ function saver(url, winMode) {
117
+ if ("download" in anchor) {
118
+ // html5 A[download]
119
+ anchor.href = url;
120
+ anchor.setAttribute("download", fileName);
121
+ anchor.className = "download-js-link";
122
+ anchor.innerHTML = "downloading...";
123
+ anchor.style.display = "none";
124
+ document.body.appendChild(anchor);
125
+ setTimeout(function () {
126
+ anchor.click();
127
+ document.body.removeChild(anchor);
128
+ if (winMode === true) {
129
+ setTimeout(function () {
130
+ self.URL.revokeObjectURL(anchor.href);
131
+ }, 250);
132
+ }
133
+ }, 66);
134
+ return true;
135
+ }
136
+
137
+ // handle non-a[download] safari as best we can:
138
+ if (
139
+ /(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)
140
+ ) {
141
+ url = url.replace(/^data:([\w/\-+]+)/, defaultMime);
142
+ if (!window.open(url)) {
143
+ // popup blocked, offer direct download:
144
+ if (
145
+ confirm(
146
+ "Displaying New Document\n\nUse Save As... to download, then click back to return to this page."
147
+ )
148
+ ) {
149
+ location.href = url;
150
+ }
151
+ }
152
+ return true;
153
+ }
154
+
155
+ // do iframe dataURL download (old ch+FF):
156
+ var f = document.createElement("iframe");
157
+ document.body.appendChild(f);
158
+
159
+ if (!winMode) {
160
+ // force a mime that will download:
161
+ url = "data:" + url.replace(/^data:([\w/\-+]+)/, defaultMime);
162
+ }
163
+ f.src = url;
164
+ setTimeout(function () {
165
+ document.body.removeChild(f);
166
+ }, 333);
167
+ } // end saver
168
+
169
+ // @ts-ignore
170
+ if (navigator.msSaveBlob) {
171
+ // IE10+ : (has Blob, but not a[download] or URL)
172
+ // @ts-ignore
173
+ return navigator.msSaveBlob(blob, fileName);
174
+ }
175
+
176
+ if (self.URL) {
177
+ // simple fast and modern way using Blob and URL:
178
+ saver(self.URL.createObjectURL(blob), true);
179
+ } else {
180
+ // handle non-Blob()+non-URL browsers:
181
+ if (typeof blob === "string" || blob.constructor === toString) {
182
+ try {
183
+ // @ts-ignore
184
+ return saver("data:" + mimeType + ";base64," + self.btoa(blob));
185
+ } catch (y) {
186
+ // @ts-ignore
187
+ return saver("data:" + mimeType + "," + encodeURIComponent(blob));
188
+ }
189
+ }
190
+
191
+ // Blob but not URL support:
192
+ reader = new FileReader();
193
+ reader.onload = function (e) {
194
+ // @ts-ignore
195
+ saver(this.result);
196
+ };
197
+ reader.readAsDataURL(blob);
198
+ }
199
+ return true;
200
+ }