parse-dashboard 8.2.0-alpha.8 → 8.2.0

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 (39) hide show
  1. package/Parse-Dashboard/browser-control/BrowserControlAPI.js +468 -0
  2. package/Parse-Dashboard/browser-control/BrowserEventStream.js +294 -0
  3. package/Parse-Dashboard/browser-control/BrowserSessionManager.js +304 -0
  4. package/Parse-Dashboard/browser-control/README.md +852 -0
  5. package/Parse-Dashboard/browser-control/ServerOrchestrator.js +334 -0
  6. package/Parse-Dashboard/browser-control/index.js +27 -0
  7. package/Parse-Dashboard/browser-control/setup.js +405 -0
  8. package/Parse-Dashboard/public/bundles/120.bundle.js +1 -1
  9. package/Parse-Dashboard/public/bundles/183.bundle.js +1 -1
  10. package/Parse-Dashboard/public/bundles/19.bundle.js +1 -1
  11. package/Parse-Dashboard/public/bundles/221.bundle.js +1 -1
  12. package/Parse-Dashboard/public/bundles/372.bundle.js +1 -1
  13. package/Parse-Dashboard/public/bundles/448.bundle.js +1 -1
  14. package/Parse-Dashboard/public/bundles/573.bundle.js +1 -1
  15. package/Parse-Dashboard/public/bundles/578.bundle.js +1 -1
  16. package/Parse-Dashboard/public/bundles/584.bundle.js +1 -1
  17. package/Parse-Dashboard/public/bundles/629.bundle.js +1 -1
  18. package/Parse-Dashboard/public/bundles/643.bundle.js +1 -1
  19. package/Parse-Dashboard/public/bundles/647.bundle.js +1 -1
  20. package/Parse-Dashboard/public/bundles/701.bundle.js +1 -1
  21. package/Parse-Dashboard/public/bundles/729.bundle.js +1 -1
  22. package/Parse-Dashboard/public/bundles/75.bundle.js +1 -1
  23. package/Parse-Dashboard/public/bundles/753.bundle.js +1 -1
  24. package/Parse-Dashboard/public/bundles/817.bundle.js +1 -1
  25. package/Parse-Dashboard/public/bundles/844.bundle.js +1 -1
  26. package/Parse-Dashboard/public/bundles/862.bundle.js +1 -1
  27. package/Parse-Dashboard/public/bundles/866.bundle.js +1 -1
  28. package/Parse-Dashboard/public/bundles/874.bundle.js +1 -1
  29. package/Parse-Dashboard/public/bundles/881.bundle.js +1 -1
  30. package/Parse-Dashboard/public/bundles/929.bundle.js +1 -1
  31. package/Parse-Dashboard/public/bundles/950.bundle.js +1 -1
  32. package/Parse-Dashboard/public/bundles/97.bundle.js +1 -1
  33. package/Parse-Dashboard/public/bundles/976.bundle.js +1 -1
  34. package/Parse-Dashboard/public/bundles/dashboard.bundle.js +1 -1
  35. package/Parse-Dashboard/public/bundles/dashboard.bundle.js.LICENSE.txt +23 -0
  36. package/Parse-Dashboard/public/bundles/login.bundle.js +1 -1
  37. package/Parse-Dashboard/server.js +33 -2
  38. package/README.md +43 -0
  39. package/package.json +18 -9
@@ -0,0 +1,405 @@
1
+ /**
2
+ * Browser Control Setup - Development Mode Only
3
+ *
4
+ * This module handles initialization of the browser-control feature for AI agents.
5
+ * It is designed to be completely isolated from production code.
6
+ *
7
+ * SECURITY: This module will NOT load in production environments (NODE_ENV=production)
8
+ */
9
+
10
+ const { spawn, execSync } = require('child_process');
11
+ const net = require('net');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Webpack compilation state - shared across the module
16
+ * Tracks whether webpack is currently compiling or idle
17
+ */
18
+ const webpackState = {
19
+ isCompiling: true, // Start as true since initial compilation hasn't happened
20
+ lastCompilationTime: null,
21
+ lastCompilationDuration: null,
22
+ errors: [],
23
+ warnings: [],
24
+ compileCount: 0,
25
+ };
26
+
27
+ /**
28
+ * Get the current webpack compilation state
29
+ * @returns {Object} Current webpack state
30
+ */
31
+ function getWebpackState() {
32
+ return { ...webpackState };
33
+ }
34
+
35
+ /**
36
+ * Start webpack in watch mode with compilation state tracking
37
+ * @returns {Object} Webpack compiler instance
38
+ */
39
+ function startWebpackWatchMode() {
40
+ const webpack = require('webpack');
41
+ const webpackConfig = require(path.resolve(__dirname, '../../webpack/build.config.js'));
42
+
43
+ const compiler = webpack(webpackConfig);
44
+
45
+ // Track compilation start
46
+ compiler.hooks.watchRun.tap('BrowserControlPlugin', () => {
47
+ webpackState.isCompiling = true;
48
+ webpackState.compileCount++;
49
+ webpackState.compilationStartTime = Date.now();
50
+ console.log(`[Webpack] Compilation #${webpackState.compileCount} started...`);
51
+ });
52
+
53
+ // Track compilation end
54
+ compiler.hooks.done.tap('BrowserControlPlugin', (stats) => {
55
+ const duration = Date.now() - webpackState.compilationStartTime;
56
+ webpackState.isCompiling = false;
57
+ webpackState.lastCompilationTime = Date.now();
58
+ webpackState.lastCompilationDuration = duration;
59
+ webpackState.errors = stats.hasErrors() ? stats.toJson().errors.map(e => e.message || e) : [];
60
+ webpackState.warnings = stats.hasWarnings() ? stats.toJson().warnings.length : 0;
61
+
62
+ if (stats.hasErrors()) {
63
+ console.log(`[Webpack] Compilation #${webpackState.compileCount} failed with errors (${duration}ms)`);
64
+ } else {
65
+ console.log(`[Webpack] Compilation #${webpackState.compileCount} completed in ${duration}ms`);
66
+ }
67
+ });
68
+
69
+ // Start watching
70
+ const watcher = compiler.watch({
71
+ aggregateTimeout: 300,
72
+ poll: undefined,
73
+ }, (err) => {
74
+ if (err) {
75
+ console.error('[Webpack] Fatal error:', err);
76
+ webpackState.isCompiling = false;
77
+ webpackState.errors = [err.message];
78
+ }
79
+ });
80
+
81
+ return { compiler, watcher };
82
+ }
83
+
84
+ /**
85
+ * Check if a port is in use by attempting to connect to it
86
+ * @param {number} port - Port number to check
87
+ * @returns {Promise<boolean>} - True if port is in use, false otherwise
88
+ */
89
+ async function isPortInUse(port) {
90
+ return new Promise((resolve) => {
91
+ const socket = new net.Socket();
92
+ socket.setTimeout(1000);
93
+
94
+ socket.on('connect', () => {
95
+ socket.destroy();
96
+ resolve(true);
97
+ });
98
+
99
+ socket.on('timeout', () => {
100
+ socket.destroy();
101
+ resolve(false);
102
+ });
103
+
104
+ socket.on('error', () => {
105
+ socket.destroy();
106
+ resolve(false);
107
+ });
108
+
109
+ socket.connect(port, '127.0.0.1');
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Get process info using the port (for error messages)
115
+ * @param {number} port - Port number
116
+ * @returns {string|null} - Process info or null if not found
117
+ */
118
+ function getProcessOnPort(port) {
119
+ try {
120
+ const result = execSync(`lsof -i :${port} -t 2>/dev/null || true`, { encoding: 'utf8' }).trim();
121
+ if (result) {
122
+ const pid = result.split('\n')[0];
123
+ const processInfo = execSync(`ps -p ${pid} -o comm= 2>/dev/null || true`, { encoding: 'utf8' }).trim();
124
+ return processInfo ? `${processInfo} (PID: ${pid})` : `PID: ${pid}`;
125
+ }
126
+ } catch {
127
+ // Silently fail - process info is optional
128
+ }
129
+ return null;
130
+ }
131
+
132
+ /**
133
+ * Initialize browser control for the dashboard
134
+ * @param {Express.Application} app - Express app instance
135
+ * @param {Object} config - Dashboard configuration
136
+ * @returns {Object|null} - Setup result with initialization hook, or null if disabled
137
+ */
138
+ function setupBrowserControl(app, config) {
139
+ const isProduction = process.env.NODE_ENV === 'production';
140
+ const configAllowsBrowserControl = config.data.browserControl === true;
141
+ const envAllowsBrowserControl = process.env.PARSE_DASHBOARD_BROWSER_CONTROL === 'true';
142
+ const explicitlyEnabled = configAllowsBrowserControl || envAllowsBrowserControl;
143
+
144
+ const shouldEnable = explicitlyEnabled && !isProduction;
145
+
146
+ if (explicitlyEnabled && isProduction) {
147
+ console.error('⚠️ SECURITY WARNING: Browser Control API cannot be enabled in production (NODE_ENV=production)');
148
+ console.error('⚠️ This is a development-only feature.');
149
+ return null;
150
+ }
151
+
152
+ if (!shouldEnable) {
153
+ return null;
154
+ }
155
+
156
+ let parseServerProcess = null;
157
+ let mongoDBInstance = null;
158
+ let browserControlAPI = null;
159
+ let browserEventStream = null;
160
+ let webpackWatcher = null;
161
+
162
+ // Auto-start MongoDB and Parse Server
163
+ const { MongoCluster } = require('mongodb-runner');
164
+ const mongoPort = parseInt(process.env.MONGO_PORT, 10) || 27017;
165
+ const parseServerPort = parseInt(process.env.PARSE_SERVER_PORT, 10) || 1337;
166
+ const parseServerURL = `http://localhost:${parseServerPort}/parse`;
167
+
168
+ // Use credentials from config file if available, otherwise fall back to env vars or defaults
169
+ const firstApp = config.data.apps && config.data.apps.length > 0 ? config.data.apps[0] : null;
170
+ const parseServerAppId = process.env.PARSE_SERVER_APP_ID || (firstApp ? firstApp.appId : 'testAppId');
171
+ const parseServerMasterKey = process.env.PARSE_SERVER_MASTER_KEY || (firstApp ? firstApp.masterKey : 'testMasterKey');
172
+ const mongoVersion = process.env.MONGO_VERSION || '8.0.4';
173
+
174
+ // Configure dashboard synchronously before parseDashboard middleware is mounted
175
+ // to ensure the dashboard knows about the auto-started Parse Server
176
+ if (!config.data.apps || config.data.apps.length === 0) {
177
+ config.data.apps = [{
178
+ serverURL: parseServerURL,
179
+ appId: parseServerAppId,
180
+ masterKey: parseServerMasterKey,
181
+ appName: 'Browser Control Test App'
182
+ }];
183
+ console.log('Dashboard auto-configured with test app');
184
+ } else {
185
+ // Update existing first app's serverURL to point to the auto-started Parse Server
186
+ config.data.apps[0].serverURL = parseServerURL;
187
+ console.log(`Dashboard configured to use Parse Server at ${parseServerURL}`);
188
+ }
189
+
190
+ // Wait for MongoDB to be ready by polling for successful connections
191
+ // Throws an error if MongoDB is not ready after maxRetries attempts
192
+ const waitForMongo = async (mongoUri, maxRetries = 20, delayMs = 500) => {
193
+ const { MongoClient } = require('mongodb');
194
+
195
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
196
+ try {
197
+ const client = new MongoClient(mongoUri, {
198
+ serverSelectionTimeoutMS: 1000,
199
+ connectTimeoutMS: 1000
200
+ });
201
+
202
+ await client.connect();
203
+ await client.close();
204
+
205
+ console.log(`MongoDB ready after ${attempt} attempt(s)`);
206
+ return;
207
+ } catch (error) {
208
+ if (attempt === maxRetries) {
209
+ throw new Error(
210
+ `MongoDB not ready after ${maxRetries} attempts (${maxRetries * delayMs}ms): ${error.message}`
211
+ );
212
+ }
213
+ // Wait before next attempt
214
+ await new Promise(resolve => setTimeout(resolve, delayMs));
215
+ }
216
+ }
217
+ };
218
+
219
+ // Start MongoDB first
220
+ const startMongoDB = async () => {
221
+ // Security check: Abort if MongoDB port is already in use
222
+ if (await isPortInUse(mongoPort)) {
223
+ const processInfo = getProcessOnPort(mongoPort);
224
+ console.error(`\n⛔ SECURITY: MongoDB port ${mongoPort} is already in use${processInfo ? ` by ${processInfo}` : ''}`);
225
+ console.error('⛔ Browser Control will not start to prevent potential conflicts or security issues.');
226
+ console.error('⛔ Please stop the existing process or use a different port via MONGO_PORT environment variable.\n');
227
+ return null;
228
+ }
229
+
230
+ try {
231
+ console.log(`Starting MongoDB ${mongoVersion} instance on port ${mongoPort}...`);
232
+ const os = require('os');
233
+ mongoDBInstance = await MongoCluster.start({
234
+ topology: 'standalone',
235
+ version: mongoVersion,
236
+ tmpDir: os.tmpdir(),
237
+ args: ['--port', mongoPort.toString()],
238
+ });
239
+ const mongoUri = mongoDBInstance.connectionString;
240
+ console.log(`MongoDB ${mongoVersion} started at ${mongoUri}`);
241
+ return mongoUri;
242
+ } catch (error) {
243
+ console.warn('Failed to start MongoDB:', error.message);
244
+ console.warn('Attempting to use existing MongoDB connection...');
245
+ return null;
246
+ }
247
+ };
248
+
249
+ // Start Parse Server after MongoDB is ready
250
+ const startParseServer = async (mongoUri) => {
251
+ // Security check: Abort if Parse Server port is already in use
252
+ if (await isPortInUse(parseServerPort)) {
253
+ const processInfo = getProcessOnPort(parseServerPort);
254
+ console.error(`\n⛔ SECURITY: Parse Server port ${parseServerPort} is already in use${processInfo ? ` by ${processInfo}` : ''}`);
255
+ console.error('⛔ Browser Control will not start Parse Server to prevent potential conflicts or security issues.');
256
+ console.error('⛔ Please stop the existing process or use a different port via PARSE_SERVER_PORT environment variable.\n');
257
+ return;
258
+ }
259
+
260
+ try {
261
+ console.log(`Starting Parse Server for browser control on port ${parseServerPort}...`);
262
+ parseServerProcess = spawn('npx', [
263
+ 'parse-server',
264
+ '--appId', parseServerAppId,
265
+ '--masterKey', parseServerMasterKey,
266
+ '--databaseURI', mongoUri,
267
+ '--port', parseServerPort.toString(),
268
+ '--serverURL', parseServerURL,
269
+ '--mountPath', '/parse'
270
+ ], {
271
+ stdio: ['ignore', 'pipe', 'pipe']
272
+ });
273
+
274
+ // Listen for Parse Server output
275
+ parseServerProcess.stdout.on('data', (data) => {
276
+ const output = data.toString();
277
+ if (output.includes('parse-server running') || output.includes('listening on port')) {
278
+ console.log(`Parse Server started at ${parseServerURL}`);
279
+ }
280
+ });
281
+
282
+ parseServerProcess.stderr.on('data', (data) => {
283
+ const error = data.toString();
284
+ // Only log actual errors, not warnings
285
+ if (error.includes('error') || error.includes('Error')) {
286
+ console.error('[Parse Server]:', error);
287
+ }
288
+ });
289
+
290
+ parseServerProcess.on('exit', (code) => {
291
+ if (code !== 0 && code !== null) {
292
+ console.error(`Parse Server exited with code ${code}`);
293
+ }
294
+ });
295
+
296
+ parseServerProcess.on('error', (err) => {
297
+ console.error(`Failed to spawn Parse Server: ${err.message}`);
298
+ console.error('This may happen if npx is not available or parse-server is not installed.');
299
+ console.error('Browser control will work but you need to configure apps manually.');
300
+ parseServerProcess = null;
301
+ });
302
+ } catch (error) {
303
+ console.warn('Failed to start Parse Server:', error.message);
304
+ console.warn('Browser control will work but you need to configure apps manually');
305
+ }
306
+ };
307
+
308
+ // Start MongoDB, wait for it to be ready, then start Parse Server
309
+ startMongoDB()
310
+ .then(async (mongoUri) => {
311
+ if (!mongoUri) {
312
+ return;
313
+ }
314
+
315
+ try {
316
+ // Wait for MongoDB to accept connections before starting Parse Server
317
+ await waitForMongo(mongoUri);
318
+ await startParseServer(mongoUri);
319
+ } catch (error) {
320
+ console.error('Failed to connect to MongoDB:', error.message);
321
+ console.error('Parse Server will not be started');
322
+ }
323
+ })
324
+ .catch(error => {
325
+ console.error('Error in MongoDB startup sequence:', error.message);
326
+ });
327
+
328
+ // Start webpack in watch mode with state tracking
329
+ try {
330
+ const webpackResult = startWebpackWatchMode();
331
+ webpackWatcher = webpackResult.watcher;
332
+ console.log('Webpack watch mode started with state tracking');
333
+ } catch (error) {
334
+ console.warn('Failed to start webpack watch mode:', error.message);
335
+ // Mark webpack as not compiling if we couldn't start it
336
+ webpackState.isCompiling = false;
337
+ }
338
+
339
+ // Load browser control API BEFORE parseDashboard middleware to bypass authentication
340
+ try {
341
+ const createBrowserControlAPI = require('./BrowserControlAPI');
342
+ browserControlAPI = createBrowserControlAPI(getWebpackState);
343
+ app.use('/browser-control', browserControlAPI);
344
+ console.log('Browser Control API enabled at /browser-control');
345
+ } catch (error) {
346
+ console.warn('Failed to load Browser Control API:', error.message);
347
+ }
348
+
349
+ // Cleanup function for servers
350
+ const cleanup = () => {
351
+ // Shutdown WebSocket server
352
+ if (browserEventStream) {
353
+ browserEventStream.shutdown().catch(err => {
354
+ console.warn('Error shutting down Browser Event Stream:', err.message);
355
+ });
356
+ }
357
+
358
+ // Stop webpack watcher
359
+ if (webpackWatcher) {
360
+ console.log('Stopping webpack watcher...');
361
+ webpackWatcher.close(() => {
362
+ console.log('Webpack watcher stopped');
363
+ });
364
+ }
365
+
366
+ if (parseServerProcess) {
367
+ console.log('Stopping Parse Server...');
368
+ parseServerProcess.kill('SIGTERM');
369
+ // Force kill after 5 seconds if still running
370
+ setTimeout(() => {
371
+ if (parseServerProcess && !parseServerProcess.killed) {
372
+ parseServerProcess.kill('SIGKILL');
373
+ }
374
+ }, 5000);
375
+ }
376
+ if (mongoDBInstance) {
377
+ console.log('Stopping MongoDB...');
378
+ mongoDBInstance.close().catch(err => {
379
+ console.warn('Error stopping MongoDB:', err.message);
380
+ });
381
+ }
382
+ };
383
+
384
+ // Hook to initialize WebSocket when HTTP server is ready
385
+ const initializeWebSocket = (server) => {
386
+ if (!browserControlAPI) {
387
+ return;
388
+ }
389
+
390
+ try {
391
+ const BrowserEventStream = require('./BrowserEventStream');
392
+ browserEventStream = new BrowserEventStream(server, browserControlAPI.sessionManager);
393
+ } catch (error) {
394
+ console.warn('Failed to initialize Browser Event Stream:', error.message);
395
+ }
396
+ };
397
+
398
+ // Return setup result with hooks
399
+ return {
400
+ cleanup,
401
+ initializeWebSocket
402
+ };
403
+ }
404
+
405
+ module.exports = setupBrowserControl;
@@ -1 +1 @@
1
- "use strict";(self.webpackChunk=self.webpackChunk||[]).push([[120],{71120:(e,o,n)=>{n.d(o,{a:()=>u,d:()=>l});var t=n(38950),i=Object.defineProperty,r=(e,o)=>i(e,"name",{value:o,configurable:!0});function a(e,o){return o.forEach((function(o){o&&"string"!=typeof o&&!Array.isArray(o)&&Object.keys(o).forEach((function(n){if("default"!==n&&!(n in e)){var t=Object.getOwnPropertyDescriptor(o,n);Object.defineProperty(e,n,t.get?t:{enumerable:!0,get:function(){return o[n]}})}}))})),Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}r(a,"_mergeNamespaces");var u={exports:{}};!function(e){function o(o,n,t){var i,r=o.getWrapperElement();return(i=r.appendChild(document.createElement("div"))).className=t?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof n?i.innerHTML=n:i.appendChild(n),e.addClass(r,"dialog-opened"),i}function n(e,o){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=o}r(o,"dialogDiv"),r(n,"closeNotification"),e.defineExtension("openDialog",(function(t,i,a){a||(a={}),n(this,null);var u=o(this,t,a.bottom),l=!1,c=this;function s(o){if("string"==typeof o)d.value=o;else{if(l)return;l=!0,e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u),c.focus(),a.onClose&&a.onClose(u)}}r(s,"close");var f,d=u.getElementsByTagName("input")[0];return d?(d.focus(),a.value&&(d.value=a.value,!1!==a.selectValueOnOpen&&d.select()),a.onInput&&e.on(d,"input",(function(e){a.onInput(e,d.value,s)})),a.onKeyUp&&e.on(d,"keyup",(function(e){a.onKeyUp(e,d.value,s)})),e.on(d,"keydown",(function(o){a&&a.onKeyDown&&a.onKeyDown(o,d.value,s)||((27==o.keyCode||!1!==a.closeOnEnter&&13==o.keyCode)&&(d.blur(),e.e_stop(o),s()),13==o.keyCode&&i(d.value,o))})),!1!==a.closeOnBlur&&e.on(u,"focusout",(function(e){null!==e.relatedTarget&&s()}))):(f=u.getElementsByTagName("button")[0])&&(e.on(f,"click",(function(){s(),c.focus()})),!1!==a.closeOnBlur&&e.on(f,"blur",s),f.focus()),s})),e.defineExtension("openConfirm",(function(t,i,a){n(this,null);var u=o(this,t,a&&a.bottom),l=u.getElementsByTagName("button"),c=!1,s=this,f=1;function d(){c||(c=!0,e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u),s.focus())}r(d,"close"),l[0].focus();for(var p=0;p<l.length;++p){var g=l[p];!function(o){e.on(g,"click",(function(n){e.e_preventDefault(n),d(),o&&o(s)}))}(i[p]),e.on(g,"blur",(function(){--f,setTimeout((function(){f<=0&&d()}),200)})),e.on(g,"focus",(function(){++f}))}})),e.defineExtension("openNotification",(function(t,i){n(this,s);var a,u=o(this,t,i&&i.bottom),l=!1,c=i&&void 0!==i.duration?i.duration:5e3;function s(){l||(l=!0,clearTimeout(a),e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u))}return r(s,"close"),e.on(u,"click",(function(o){e.e_preventDefault(o),s()})),c&&(a=setTimeout(s,c)),s}))}(t.a.exports);var l=a({__proto__:null,default:u.exports},[u.exports])}}]);
1
+ "use strict";(self.webpackChunk=self.webpackChunk||[]).push([[120],{71120(e,o,n){n.d(o,{a:()=>u,d:()=>l});var t=n(38950),i=Object.defineProperty,r=(e,o)=>i(e,"name",{value:o,configurable:!0});function a(e,o){return o.forEach(function(o){o&&"string"!=typeof o&&!Array.isArray(o)&&Object.keys(o).forEach(function(n){if("default"!==n&&!(n in e)){var t=Object.getOwnPropertyDescriptor(o,n);Object.defineProperty(e,n,t.get?t:{enumerable:!0,get:function(){return o[n]}})}})}),Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}r(a,"_mergeNamespaces");var u={exports:{}};!function(e){function o(o,n,t){var i,r=o.getWrapperElement();return(i=r.appendChild(document.createElement("div"))).className=t?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof n?i.innerHTML=n:i.appendChild(n),e.addClass(r,"dialog-opened"),i}function n(e,o){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=o}r(o,"dialogDiv"),r(n,"closeNotification"),e.defineExtension("openDialog",function(t,i,a){a||(a={}),n(this,null);var u=o(this,t,a.bottom),l=!1,c=this;function s(o){if("string"==typeof o)d.value=o;else{if(l)return;l=!0,e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u),c.focus(),a.onClose&&a.onClose(u)}}r(s,"close");var f,d=u.getElementsByTagName("input")[0];return d?(d.focus(),a.value&&(d.value=a.value,!1!==a.selectValueOnOpen&&d.select()),a.onInput&&e.on(d,"input",function(e){a.onInput(e,d.value,s)}),a.onKeyUp&&e.on(d,"keyup",function(e){a.onKeyUp(e,d.value,s)}),e.on(d,"keydown",function(o){a&&a.onKeyDown&&a.onKeyDown(o,d.value,s)||((27==o.keyCode||!1!==a.closeOnEnter&&13==o.keyCode)&&(d.blur(),e.e_stop(o),s()),13==o.keyCode&&i(d.value,o))}),!1!==a.closeOnBlur&&e.on(u,"focusout",function(e){null!==e.relatedTarget&&s()})):(f=u.getElementsByTagName("button")[0])&&(e.on(f,"click",function(){s(),c.focus()}),!1!==a.closeOnBlur&&e.on(f,"blur",s),f.focus()),s}),e.defineExtension("openConfirm",function(t,i,a){n(this,null);var u=o(this,t,a&&a.bottom),l=u.getElementsByTagName("button"),c=!1,s=this,f=1;function d(){c||(c=!0,e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u),s.focus())}r(d,"close"),l[0].focus();for(var p=0;p<l.length;++p){var g=l[p];(function(o){e.on(g,"click",function(n){e.e_preventDefault(n),d(),o&&o(s)})})(i[p]),e.on(g,"blur",function(){--f,setTimeout(function(){f<=0&&d()},200)}),e.on(g,"focus",function(){++f})}}),e.defineExtension("openNotification",function(t,i){n(this,s);var a,u=o(this,t,i&&i.bottom),l=!1,c=i&&void 0!==i.duration?i.duration:5e3;function s(){l||(l=!0,clearTimeout(a),e.rmClass(u.parentNode,"dialog-opened"),u.parentNode.removeChild(u))}return r(s,"close"),e.on(u,"click",function(o){e.e_preventDefault(o),s()}),c&&(a=setTimeout(s,c)),s})}(t.a.exports);var l=a({__proto__:null,default:u.exports},[u.exports])}}]);