PluginProbe
Code Snippets / trunk
Code Snippets vtrunk
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
← All changes | js/utils/files.ts +100 -1 3.9.6 → trunk View file →
@@ -4,8 +4,12 @@
4 4
5 5 const SECOND_IN_MS = 1000
6 6 const TIMEOUT_SECONDS = 40
7 7 const JSON_INDENT_SPACES = 2
8 +const EXPORT_FILENAME = 'snippets'
9 +const EXPORT_GENERATOR = 'Code Snippets'
10 +const DEFAULT_PRIORITY = 10
11 +const EXPORT_DATE_LENGTH = 16
8 12
9 13 const MIME_INFO = <const> {
10 14 php: ['php', 'text/php'],
11 15 html: ['php', 'text/php'],
@@ -20,9 +24,18 @@
20 24 link.download = filename
21 25 link.href = URL.createObjectURL(new Blob([content], { type }))
22 26
23 27 setTimeout(() => URL.revokeObjectURL(link.href), TIMEOUT_SECONDS * SECOND_IN_MS)
24 - setTimeout(() => link.click(), 0)
28 +
29 + // Some browsers (notably headless Chromium) can ignore programmatic clicks on detached anchors.
30 + // Appending the link to the DOM before clicking improves reliability.
31 + link.style.display = 'none'
32 + document.body.appendChild(link)
33 +
34 + setTimeout(() => {
35 + link.click()
36 + link.remove()
37 + }, 0)
25 38 }
26 39
27 40 export const downloadSnippetExportFile = (
28 41 content: SnippetsExport | string,
@@ -39,5 +52,91 @@
39 52 } else {
40 53 const filename = `${title}.code-snippets.json`
41 54 downloadAsFile(JSON.stringify(content, undefined, JSON_INDENT_SPACES), filename, 'application/json')
42 55 }
56 +}
57 +
58 +type DefaultExportValueCheck = (value: unknown) => boolean
59 +
60 +const isNullish = (value: unknown): value is undefined | null => undefined === value || null === value
61 +
62 +const DEFAULT_EXPORT_VALUE_CHECKS: Record<string, DefaultExportValueCheck> = {
63 + id: value => 0 === value,
64 + desc: value => '' === value,
65 + name: value => '' === value,
66 + code: value => '' === value,
67 + tags: value => Array.isArray(value) && 0 === value.length,
68 + scope: value => 'global' === value,
69 + condition_id: value => 0 === value,
70 + active: value => false === value,
71 + locked: value => false === value,
72 + trashed: value => false === value,
73 + priority: value => DEFAULT_PRIORITY === value,
74 + modified: value => '' === value || isNullish(value),
75 + last_active: value => 0 === value || isNullish(value),
76 + network: value => null === value || false === value,
77 + shared_network: value => null === value || false === value,
78 + code_error: value => null === value || false === value,
79 + code_error_trace: value => null === value || false === value
80 +}
81 +
82 +const isDefaultExportValue = (field: string, value: unknown): boolean =>
83 + (DEFAULT_EXPORT_VALUE_CHECKS[field] ?? isNullish)(value)
84 +
85 +const buildExportSnippet = ({
86 + id,
87 + name,
88 + desc,
89 + code,
90 + tags,
91 + scope,
92 + active,
93 + locked,
94 + trashed,
95 + network,
96 + shared_network,
97 + modified,
98 + priority,
99 + conditionId,
100 + lastActive,
101 + code_error,
102 + code_error_trace
103 +}: Snippet): SnippetsExport['snippets'][number] =>
104 + Object.fromEntries(
105 + Object.entries({
106 + id,
107 + name,
108 + desc,
109 + code,
110 + tags,
111 + scope,
112 + active,
113 + locked,
114 + trashed,
115 + network,
116 + shared_network,
117 + modified,
118 + priority,
119 + condition_id: conditionId,
120 + last_active: lastActive,
121 + code_error,
122 + code_error_trace
123 + }).filter(([field, value]) => !isDefaultExportValue(field, value))
124 + )
125 +
126 +export const downloadBulkSnippetExportFile = (snippets: readonly Snippet[]) => {
127 + if (0 === snippets.length) {
128 + return
129 + }
130 +
131 + const content: SnippetsExport = {
132 + generator: EXPORT_GENERATOR,
133 + date_created: new Date().toISOString().slice(0, EXPORT_DATE_LENGTH).replace('T', ' '),
134 + snippets: snippets.map(buildExportSnippet)
135 + }
136 +
137 + downloadAsFile(
138 + JSON.stringify(content, undefined, JSON_INDENT_SPACES),
139 + `${EXPORT_FILENAME}.code-snippets.json`,
140 + 'application/json'
141 + )
43 142 }