/
dialog.js
270 lines (222 loc) · 7.84 KB
/
dialog.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
'use strict';
const { app, BrowserWindow, deprecate } = require('electron');
const binding = process.electronBinding('dialog');
const v8Util = process.electronBinding('v8_util');
const DialogType = {
OPEN: 'OPEN',
SAVE: 'SAVE'
};
const saveFileDialogProperties = {
createDirectory: 1 << 0,
showHiddenFiles: 1 << 1,
treatPackageAsDirectory: 1 << 2,
showOverwriteConfirmation: 1 << 3,
dontAddToRecent: 1 << 4
};
const openFileDialogProperties = {
openFile: 1 << 0,
openDirectory: 1 << 1,
multiSelections: 1 << 2,
createDirectory: 1 << 3, // macOS
showHiddenFiles: 1 << 4,
promptToCreate: 1 << 5, // Windows
noResolveAliases: 1 << 6, // macOS
treatPackageAsDirectory: 1 << 7, // macOS
dontAddToRecent: 1 << 8 // Windows
};
const normalizeAccessKey = (text) => {
if (typeof text !== 'string') return text;
// macOS does not have access keys so remove single ampersands
// and replace double ampersands with a single ampersand
if (process.platform === 'darwin') {
return text.replace(/&(&?)/g, '$1');
}
// Linux uses a single underscore as an access key prefix so escape
// existing single underscores with a second underscore, replace double
// ampersands with a single ampersand, and replace a single ampersand with
// a single underscore
if (process.platform === 'linux') {
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
if (after === '&') return after;
return `_${after}`;
});
}
return text;
};
const checkAppInitialized = function () {
if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready');
}
};
const setupDialogProperties = (type, properties) => {
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties;
let dialogProperties = 0;
for (const prop in dialogPropertiesTypes) {
if (properties.includes(prop)) {
dialogProperties |= dialogPropertiesTypes[prop];
}
}
return dialogProperties;
};
const saveDialog = (sync, window, options) => {
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
}
if (options == null) options = { title: 'Save' };
const {
buttonLabel = '',
defaultPath = '',
filters = [],
properties = [],
title = '',
message = '',
securityScopedBookmarks = false,
nameFieldLabel = '',
showsTagField = true
} = options;
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
settings.properties = setupDialogProperties(DialogType.SAVE, properties);
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
};
const openDialog = (sync, window, options) => {
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
}
if (options == null) {
options = {
title: 'Open',
properties: ['openFile']
};
}
const {
buttonLabel = '',
defaultPath = '',
filters = [],
properties = ['openFile'],
title = '',
message = '',
securityScopedBookmarks = false
} = options;
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
settings.properties = setupDialogProperties(DialogType.OPEN, properties);
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
};
const messageBox = (sync, window, options) => {
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
}
if (options == null) options = { type: 'none' };
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
const messageBoxOptions = { noLink: 1 << 0 };
let {
buttons = [],
cancelId,
checkboxLabel = '',
checkboxChecked,
defaultId = -1,
detail = '',
icon = null,
noLink = false,
message = '',
title = '',
type = 'none'
} = options;
const messageBoxType = messageBoxTypes.indexOf(type);
if (messageBoxType === -1) throw new TypeError('Invalid message box type');
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
checkboxChecked = !!checkboxChecked;
if (checkboxChecked && !checkboxLabel) {
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
}
// Choose a default button to get selected when dialog is cancelled.
if (cancelId == null) {
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
for (let i = 0; i < buttons.length; i++) {
const text = buttons[i].toLowerCase();
if (text === 'cancel' || text === 'no') {
cancelId = i;
break;
}
}
}
const settings = {
window,
messageBoxType,
buttons,
defaultId,
cancelId,
noLink,
title,
message,
detail,
checkboxLabel,
checkboxChecked,
icon
};
if (sync) {
return binding.showMessageBoxSync(settings);
} else {
return binding.showMessageBox(settings);
}
};
module.exports = {
showOpenDialog: function (window, options) {
return openDialog(false, window, options);
},
showOpenDialogSync: function (window, options) {
return openDialog(true, window, options);
},
showSaveDialog: function (window, options) {
return saveDialog(false, window, options);
},
showSaveDialogSync: function (window, options) {
return saveDialog(true, window, options);
},
showMessageBox: function (window, options) {
return messageBox(false, window, options);
},
showMessageBoxSync: function (window, options) {
return messageBox(true, window, options);
},
showErrorBox: function (...args) {
return binding.showErrorBox(...args);
},
showCertificateTrustDialog: function (window, options) {
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
}
if (options == null || typeof options !== 'object') {
throw new TypeError('options must be an object');
}
const { certificate, message = '' } = options;
if (certificate == null || typeof certificate !== 'object') {
throw new TypeError('certificate must be an object');
}
if (typeof message !== 'string') throw new TypeError('message must be a string');
return binding.showCertificateTrustDialog(window, certificate, message);
}
};