core: Minor cleanup and style.
This commit is contained in:
		
							
								
								
									
										209
									
								
								core/client.js
									
									
									
									
									
								
							
							
						
						
									
										209
									
								
								core/client.js
									
									
									
									
									
								
							| @@ -9,18 +9,17 @@ | ||||
| import {LitElement, html, css, svg} from '/lit/lit-all.min.js'; | ||||
|  | ||||
| let cm6; | ||||
| let gSocket; | ||||
|  | ||||
| let gCurrentFile; | ||||
| let gFiles = {}; | ||||
| let gApp = {files: {}, emoji: '📦'}; | ||||
| let gEditor; | ||||
| let gOriginalInput; | ||||
| let gUnloading; | ||||
| let g_socket; | ||||
| let g_current_file; | ||||
| let g_files = {}; | ||||
| let g_app = {files: {}, emoji: '📦'}; | ||||
| let g_editor; | ||||
| let g_unloading; | ||||
|  | ||||
| let kErrorColor = '#dc322f'; | ||||
| let kDisconnectColor = '#f00'; | ||||
| let kStatusColor = '#fff'; | ||||
| let k_color_error = '#dc322f'; | ||||
| let k_color_disconnect = '#f00'; | ||||
| let k_color_status = '#fff'; | ||||
| /** \endcond */ | ||||
|  | ||||
| /** Functions that server-side app code can call through the app object. */ | ||||
| @@ -410,7 +409,7 @@ class TfNavigationElement extends LitElement { | ||||
| 							<link type="text/css" rel="stylesheet" href="/static/w3.css" /> | ||||
| 							<div | ||||
| 								class="w3-bar-item" | ||||
| 								style="color: ${this.status.color ?? kStatusColor}" | ||||
| 								style="color: ${this.status.color ?? k_color_status}" | ||||
| 							> | ||||
| 								${this.status.message} | ||||
| 							</div> | ||||
| @@ -429,7 +428,7 @@ class TfNavigationElement extends LitElement { | ||||
| 					<div class="w3-model w3-animate-top" style="position: absolute; left: 50%; transform: translate(-50%); z-index: 1"> | ||||
| 						<dijv class="w3-modal-content w3-card-4" style="display: block; padding: 1em"> | ||||
| 							<span id="close_error" @click=${self.clear_error} class="w3-button w3-display-topright">×</span> | ||||
| 							<div style="color: ${this.status.color ?? kErrorColor}"><b>ERROR:</b><p id="error" style="white-space: pre">${this.status.message}</p></div> | ||||
| 							<div style="color: ${this.status.color ?? k_color_error}"><b>ERROR:</b><p id="error" style="white-space: pre">${this.status.message}</p></div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					` | ||||
| @@ -521,7 +520,7 @@ class TfFilesElement extends LitElement { | ||||
| 		for (let file of event.dataTransfer.files) { | ||||
| 			let buffer = await file.arrayBuffer(); | ||||
| 			let text = new TextDecoder('latin1').decode(buffer); | ||||
| 			gFiles[file.name] = { | ||||
| 			g_files[file.name] = { | ||||
| 				doc: cm6.EditorState.create({ | ||||
| 					doc: text, | ||||
| 					extensions: cm6.extensions, | ||||
| @@ -529,9 +528,9 @@ class TfFilesElement extends LitElement { | ||||
| 				buffer: buffer, | ||||
| 				isNew: true, | ||||
| 			}; | ||||
| 			gCurrentFile = file.name; | ||||
| 			g_current_file = file.name; | ||||
| 		} | ||||
| 		openFile(gCurrentFile); | ||||
| 		openFile(g_current_file); | ||||
| 		updateFiles(); | ||||
| 	} | ||||
|  | ||||
| @@ -895,11 +894,11 @@ async function edit() { | ||||
| 		: 'flex'; | ||||
|  | ||||
| 	try { | ||||
| 		if (!gEditor) { | ||||
| 		if (!g_editor) { | ||||
| 			cm6 = await import('/codemirror/cm6.js'); | ||||
| 			gEditor = cm6.TildeFriendsEditorView(document.getElementById('editor')); | ||||
| 			g_editor = cm6.TildeFriendsEditorView(document.getElementById('editor')); | ||||
| 		} | ||||
| 		gEditor.onDocChange = updateFiles; | ||||
| 		g_editor.onDocChange = updateFiles; | ||||
| 		await load(); | ||||
| 	} catch (error) { | ||||
| 		alert(`${error.message}\n\n${error.stack}`); | ||||
| @@ -932,13 +931,13 @@ function loadFile(name, id) { | ||||
| 			return response.text(); | ||||
| 		}) | ||||
| 		.then(function (text) { | ||||
| 			gFiles[name].doc = cm6.EditorState.create({ | ||||
| 			g_files[name].doc = cm6.EditorState.create({ | ||||
| 				doc: text, | ||||
| 				extensions: cm6.extensions, | ||||
| 			}); | ||||
| 			gFiles[name].original = gFiles[name].doc.doc.toString(); | ||||
| 			if (!Object.values(gFiles).some((x) => !x.doc)) { | ||||
| 				openFile(Object.keys(gFiles).sort()[0]); | ||||
| 			g_files[name].original = g_files[name].doc.doc.toString(); | ||||
| 			if (!Object.values(g_files).some((x) => !x.doc)) { | ||||
| 				openFile(Object.keys(g_files).sort()[0]); | ||||
| 			} | ||||
| 		}); | ||||
| } | ||||
| @@ -956,31 +955,31 @@ async function load(path) { | ||||
| 	} else if (response.status != 404) { | ||||
| 		throw new Error(response.status + ' ' + response.statusText); | ||||
| 	} | ||||
| 	gFiles = {}; | ||||
| 	g_files = {}; | ||||
| 	let isApp = false; | ||||
| 	let promises = []; | ||||
|  | ||||
| 	if (json && json['type'] == 'tildefriends-app') { | ||||
| 		isApp = true; | ||||
| 		Object.keys(json['files']).forEach(function (name) { | ||||
| 			gFiles[name] = {}; | ||||
| 			g_files[name] = {}; | ||||
| 			promises.push(loadFile(name, json['files'][name])); | ||||
| 		}); | ||||
| 		if (Object.keys(json['files']).length == 0) { | ||||
| 			document.getElementById('editPane').style.display = 'flex'; | ||||
| 		} | ||||
| 		gApp = json; | ||||
| 		gApp.emoji = gApp.emoji || '📦'; | ||||
| 		document.getElementById('icon').innerHTML = gApp.emoji; | ||||
| 		g_app = json; | ||||
| 		g_app.emoji = g_app.emoji || '📦'; | ||||
| 		document.getElementById('icon').innerHTML = g_app.emoji; | ||||
| 	} | ||||
| 	if (!isApp) { | ||||
| 		document.getElementById('editPane').style.display = 'flex'; | ||||
| 		let text = '// New script.\n'; | ||||
| 		gCurrentFile = 'app.js'; | ||||
| 		gFiles[gCurrentFile] = { | ||||
| 		g_current_file = 'app.js'; | ||||
| 		g_files[g_current_file] = { | ||||
| 			doc: cm6.EditorState.create({doc: text, extensions: cm6.extensions}), | ||||
| 		}; | ||||
| 		openFile(gCurrentFile); | ||||
| 		openFile(g_current_file); | ||||
| 	} | ||||
| 	return Promise.all(promises); | ||||
| } | ||||
| @@ -1001,13 +1000,14 @@ function closeEditor() { | ||||
|  */ | ||||
| function save(save_to) { | ||||
| 	document.getElementById('save').disabled = true; | ||||
| 	if (gCurrentFile) { | ||||
| 		gFiles[gCurrentFile].doc = gEditor.state; | ||||
| 	if (g_current_file) { | ||||
| 		g_files[g_current_file].doc = g_editor.state; | ||||
| 		if ( | ||||
| 			!gFiles[gCurrentFile].isNew && | ||||
| 			!gFiles[gCurrentFile].doc.doc.toString() == gFiles[gCurrentFile].original | ||||
| 			!g_files[g_current_file].isNew && | ||||
| 			!g_files[g_current_file].doc.doc.toString() == | ||||
| 				g_files[g_current_file].original | ||||
| 		) { | ||||
| 			delete gFiles[gCurrentFile].buffer; | ||||
| 			delete g_files[g_current_file].buffer; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1022,8 +1022,8 @@ function save(save_to) { | ||||
| 	} | ||||
|  | ||||
| 	let promises = []; | ||||
| 	for (let name of Object.keys(gFiles)) { | ||||
| 		let file = gFiles[name]; | ||||
| 	for (let name of Object.keys(g_files)) { | ||||
| 		let file = g_files[name]; | ||||
| 		if (!file.isNew && file.doc.doc.toString() == file.original) { | ||||
| 			continue; | ||||
| 		} | ||||
| @@ -1065,14 +1065,14 @@ function save(save_to) { | ||||
| 			let app = { | ||||
| 				type: 'tildefriends-app', | ||||
| 				files: Object.fromEntries( | ||||
| 					Object.keys(gFiles).map((x) => [x, gFiles[x].id || gApp.files[x]]) | ||||
| 					Object.keys(g_files).map((x) => [x, g_files[x].id || g_app.files[x]]) | ||||
| 				), | ||||
| 				emoji: gApp.emoji || '📦', | ||||
| 				emoji: g_app.emoji || '📦', | ||||
| 			}; | ||||
| 			Object.values(gFiles).forEach(function (file) { | ||||
| 			Object.values(g_files).forEach(function (file) { | ||||
| 				delete file.id; | ||||
| 			}); | ||||
| 			gApp = JSON.parse(JSON.stringify(app)); | ||||
| 			g_app = JSON.parse(JSON.stringify(app)); | ||||
|  | ||||
| 			return fetch(save_path + 'save', { | ||||
| 				method: 'POST', | ||||
| @@ -1087,7 +1087,7 @@ function save(save_to) { | ||||
|  | ||||
| 				if (save_path != window.location.pathname) { | ||||
| 					alert('Saved to ' + save_path + '.'); | ||||
| 				} else if (!gFiles['app.js']) { | ||||
| 				} else if (!g_files['app.js']) { | ||||
| 					window.location.reload(); | ||||
| 				} else { | ||||
| 					reconnect(save_path); | ||||
| @@ -1099,7 +1099,7 @@ function save(save_to) { | ||||
| 		}) | ||||
| 		.finally(function () { | ||||
| 			document.getElementById('save').disabled = false; | ||||
| 			Object.values(gFiles).forEach(function (file) { | ||||
| 			Object.values(g_files).forEach(function (file) { | ||||
| 				file.original = file.doc.doc.toString(); | ||||
| 			}); | ||||
| 			updateFiles(); | ||||
| @@ -1112,8 +1112,8 @@ function save(save_to) { | ||||
| function changeIcon() { | ||||
| 	let value = prompt('Enter a new app icon emoji:'); | ||||
| 	if (value !== undefined) { | ||||
| 		gApp.emoji = value || '📦'; | ||||
| 		document.getElementById('icon').innerHTML = gApp.emoji; | ||||
| 		g_app.emoji = value || '📦'; | ||||
| 		document.getElementById('icon').innerHTML = g_app.emoji; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1190,9 +1190,12 @@ function api_postMessage(message) { | ||||
| function api_error(error) { | ||||
| 	if (error) { | ||||
| 		if (typeof error == 'string') { | ||||
| 			setStatusMessage('⚠️ ' + error, kErrorColor); | ||||
| 			setStatusMessage('⚠️ ' + error, k_color_error); | ||||
| 		} else { | ||||
| 			setStatusMessage('⚠️ ' + error.message + '\n' + error.stack, kErrorColor); | ||||
| 			setStatusMessage( | ||||
| 				'⚠️ ' + error.message + '\n' + error.stack, | ||||
| 				k_color_error | ||||
| 			); | ||||
| 		} | ||||
| 	} | ||||
| 	console.log('error', error); | ||||
| @@ -1309,7 +1312,7 @@ function api_setHash(hash) { | ||||
|  */ | ||||
| function _receive_websocket_message(message) { | ||||
| 	if (message && message.action == 'session') { | ||||
| 		setStatusMessage('🟢 Executing...', kStatusColor); | ||||
| 		setStatusMessage('🟢 Executing...', k_color_status); | ||||
| 		let navigation = document.getElementsByTagName('tf-navigation')[0]; | ||||
| 		navigation.credentials = message.credentials; | ||||
| 		navigation.identities = message.identities; | ||||
| @@ -1409,7 +1412,7 @@ function setStatusMessage(message, color) { | ||||
| 	document.getElementsByTagName('tf-navigation')[0].status = { | ||||
| 		message: message, | ||||
| 		color: color, | ||||
| 		is_error: color == kErrorColor, | ||||
| 		is_error: color == k_color_error, | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| @@ -1419,11 +1422,11 @@ function setStatusMessage(message, color) { | ||||
|  */ | ||||
| function send(value) { | ||||
| 	try { | ||||
| 		if (gSocket && gSocket.readyState == gSocket.OPEN) { | ||||
| 			gSocket.send(JSON.stringify(value)); | ||||
| 		if (g_socket && g_socket.readyState == g_socket.OPEN) { | ||||
| 			g_socket.send(JSON.stringify(value)); | ||||
| 		} | ||||
| 	} catch (error) { | ||||
| 		setStatusMessage('🤷 Send failed: ' + error.toString(), kErrorColor); | ||||
| 		setStatusMessage('🤷 Send failed: ' + error.toString(), k_color_error); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1438,7 +1441,7 @@ function hashChange() { | ||||
|  * Make sure the app is connected on window focus, and notify the app. | ||||
|  */ | ||||
| function focus() { | ||||
| 	if (gSocket && gSocket.readyState == gSocket.CLOSED) { | ||||
| 	if (g_socket && g_socket.readyState == g_socket.CLOSED) { | ||||
| 		connectSocket(); | ||||
| 	} else { | ||||
| 		send({event: 'focus'}); | ||||
| @@ -1449,9 +1452,7 @@ function focus() { | ||||
|  * Notify the app of lost focus. | ||||
|  */ | ||||
| function blur() { | ||||
| 	if (gSocket && gSocket.readyState == gSocket.OPEN) { | ||||
| 		send({event: 'blur'}); | ||||
| 	} | ||||
| 	send({event: 'blur'}); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -1508,8 +1509,8 @@ function message(event) { | ||||
|  * @param path The path to which the WebSocket should be connected. | ||||
|  */ | ||||
| function reconnect(path) { | ||||
| 	let oldSocket = gSocket; | ||||
| 	gSocket = null; | ||||
| 	let oldSocket = g_socket; | ||||
| 	g_socket = null; | ||||
| 	if (oldSocket) { | ||||
| 		oldSocket.onopen = null; | ||||
| 		oldSocket.onclose = null; | ||||
| @@ -1524,24 +1525,24 @@ function reconnect(path) { | ||||
|  * @param path The path to which to connect. | ||||
|  */ | ||||
| function connectSocket(path) { | ||||
| 	if (!gSocket || gSocket.readyState != gSocket.OPEN) { | ||||
| 		if (gSocket) { | ||||
| 			gSocket.onopen = null; | ||||
| 			gSocket.onclose = null; | ||||
| 			gSocket.onmessage = null; | ||||
| 			gSocket.close(); | ||||
| 	if (!g_socket || g_socket.readyState != g_socket.OPEN) { | ||||
| 		if (g_socket) { | ||||
| 			g_socket.onopen = null; | ||||
| 			g_socket.onclose = null; | ||||
| 			g_socket.onmessage = null; | ||||
| 			g_socket.close(); | ||||
| 		} | ||||
| 		setStatusMessage('⚪ Connecting...', kStatusColor); | ||||
| 		gSocket = new WebSocket( | ||||
| 		setStatusMessage('⚪ Connecting...', k_color_status); | ||||
| 		g_socket = new WebSocket( | ||||
| 			(window.location.protocol == 'https:' ? 'wss://' : 'ws://') + | ||||
| 				window.location.hostname + | ||||
| 				(window.location.port.length ? ':' + window.location.port : '') + | ||||
| 				'/app/socket' | ||||
| 		); | ||||
| 		gSocket.onopen = function () { | ||||
| 			setStatusMessage('🟡 Authenticating...', kStatusColor); | ||||
| 		g_socket.onopen = function () { | ||||
| 			setStatusMessage('🟡 Authenticating...', k_color_status); | ||||
| 			let connect_path = path ?? window.location.pathname; | ||||
| 			gSocket.send( | ||||
| 			g_socket.send( | ||||
| 				JSON.stringify({ | ||||
| 					action: 'hello', | ||||
| 					path: connect_path, | ||||
| @@ -1553,12 +1554,12 @@ function connectSocket(path) { | ||||
| 				}) | ||||
| 			); | ||||
| 		}; | ||||
| 		gSocket.onmessage = function (event) { | ||||
| 		g_socket.onmessage = function (event) { | ||||
| 			_receive_websocket_message(JSON.parse(event.data)); | ||||
| 		}; | ||||
| 		gSocket.onclose = function (event) { | ||||
| 			if (gUnloading) { | ||||
| 				setStatusMessage('⚪ Closing...', kStatusColor); | ||||
| 		g_socket.onclose = function (event) { | ||||
| 			if (g_unloading) { | ||||
| 				setStatusMessage('⚪ Closing...', k_color_status); | ||||
| 			} else { | ||||
| 				const k_codes = { | ||||
| 					1000: 'Normal closure', | ||||
| @@ -1579,7 +1580,7 @@ function connectSocket(path) { | ||||
| 				}; | ||||
| 				setStatusMessage( | ||||
| 					'🔴 Closed: ' + (k_codes[event.code] || event.code), | ||||
| 					kDisconnectColor | ||||
| 					k_color_disconnect | ||||
| 				); | ||||
| 			} | ||||
| 		}; | ||||
| @@ -1592,24 +1593,24 @@ function connectSocket(path) { | ||||
|  */ | ||||
| function openFile(name) { | ||||
| 	let newDoc = | ||||
| 		name && gFiles[name] | ||||
| 			? gFiles[name].doc | ||||
| 		name && g_files[name] | ||||
| 			? g_files[name].doc | ||||
| 			: cm6.EditorState.create({doc: '', extensions: cm6.extensions}); | ||||
| 	let oldDoc = gEditor.state; | ||||
| 	gEditor.setState(newDoc); | ||||
| 	let oldDoc = g_editor.state; | ||||
| 	g_editor.setState(newDoc); | ||||
|  | ||||
| 	if (gFiles[gCurrentFile]) { | ||||
| 		gFiles[gCurrentFile].doc = oldDoc; | ||||
| 	if (g_files[g_current_file]) { | ||||
| 		g_files[g_current_file].doc = oldDoc; | ||||
| 		if ( | ||||
| 			!gFiles[gCurrentFile].isNew && | ||||
| 			gFiles[gCurrentFile].doc.doc.toString() == oldDoc.doc.toString() | ||||
| 			!g_files[g_current_file].isNew && | ||||
| 			g_files[g_current_file].doc.doc.toString() == oldDoc.doc.toString() | ||||
| 		) { | ||||
| 			delete gFiles[gCurrentFile].buffer; | ||||
| 			delete g_files[g_current_file].buffer; | ||||
| 		} | ||||
| 	} | ||||
| 	gCurrentFile = name; | ||||
| 	g_current_file = name; | ||||
| 	updateFiles(); | ||||
| 	gEditor.focus(); | ||||
| 	g_editor.focus(); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -1619,19 +1620,19 @@ function updateFiles() { | ||||
| 	let files = document.getElementsByTagName('tf-files-pane')[0]; | ||||
| 	if (files) { | ||||
| 		files.files = Object.fromEntries( | ||||
| 			Object.keys(gFiles).map((file) => [ | ||||
| 			Object.keys(g_files).map((file) => [ | ||||
| 				file, | ||||
| 				{ | ||||
| 					clean: | ||||
| 						(file == gCurrentFile | ||||
| 							? gEditor.state.doc.toString() | ||||
| 							: gFiles[file].doc.doc.toString()) == gFiles[file].original, | ||||
| 						(file == g_current_file | ||||
| 							? g_editor.state.doc.toString() | ||||
| 							: g_files[file].doc.doc.toString()) == g_files[file].original, | ||||
| 				}, | ||||
| 			]) | ||||
| 		); | ||||
| 		files.current = gCurrentFile; | ||||
| 		files.current = g_current_file; | ||||
| 	} | ||||
| 	gEditor.focus(); | ||||
| 	g_editor.focus(); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -1639,7 +1640,7 @@ function updateFiles() { | ||||
|  * @param name The file's name. | ||||
|  */ | ||||
| function makeNewFile(name) { | ||||
| 	gFiles[name] = { | ||||
| 	g_files[name] = { | ||||
| 		doc: cm6.EditorState.create({extensions: cm6.extensions}), | ||||
| 	}; | ||||
| 	openFile(name); | ||||
| @@ -1650,7 +1651,7 @@ function makeNewFile(name) { | ||||
|  */ | ||||
| function newFile() { | ||||
| 	let name = prompt('Name of new file:', 'file.js'); | ||||
| 	if (name && !gFiles[name]) { | ||||
| 	if (name && !g_files[name]) { | ||||
| 		makeNewFile(name); | ||||
| 	} | ||||
| } | ||||
| @@ -1659,9 +1660,9 @@ function newFile() { | ||||
|  * Prompt to remove a file. | ||||
|  */ | ||||
| function removeFile() { | ||||
| 	if (confirm('Remove ' + gCurrentFile + '?')) { | ||||
| 		delete gFiles[gCurrentFile]; | ||||
| 		openFile(Object.keys(gFiles)[0]); | ||||
| 	if (confirm('Remove ' + g_current_file + '?')) { | ||||
| 		delete g_files[g_current_file]; | ||||
| 		openFile(Object.keys(g_files)[0]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1677,13 +1678,13 @@ async function appExport() { | ||||
| 		`${name}.json`, | ||||
| 		JSON.stringify({ | ||||
| 			type: 'tildefriends-app', | ||||
| 			emoji: gApp.emoji || '📦', | ||||
| 			emoji: g_app.emoji || '📦', | ||||
| 		}) | ||||
| 	); | ||||
| 	for (let file of Object.keys(gFiles)) { | ||||
| 	for (let file of Object.keys(g_files)) { | ||||
| 		zip.file( | ||||
| 			`${name}/${file}`, | ||||
| 			gFiles[file].buffer ?? gFiles[file].doc.doc.toString() | ||||
| 			g_files[file].buffer ?? g_files[file].doc.doc.toString() | ||||
| 		); | ||||
| 	} | ||||
| 	let content = await zip.generateAsync({ | ||||
| @@ -1802,9 +1803,9 @@ async function sourcePretty() { | ||||
| 	let babel = (await import('/prettier/babel.mjs')).default; | ||||
| 	let estree = (await import('/prettier/estree.mjs')).default; | ||||
| 	let prettier_html = (await import('/prettier/html.mjs')).default; | ||||
| 	let source = gEditor.state.doc.toString(); | ||||
| 	let source = g_editor.state.doc.toString(); | ||||
| 	let formatted = await prettier.format(source, { | ||||
| 		parser: gCurrentFile?.toLowerCase()?.endsWith('.html') ? 'html' : 'babel', | ||||
| 		parser: g_current_file?.toLowerCase()?.endsWith('.html') ? 'html' : 'babel', | ||||
| 		plugins: [babel, estree, prettier_html], | ||||
| 		trailingComma: 'es5', | ||||
| 		useTabs: true, | ||||
| @@ -1813,10 +1814,10 @@ async function sourcePretty() { | ||||
| 		bracketSpacing: false, | ||||
| 	}); | ||||
| 	if (source !== formatted) { | ||||
| 		gEditor.dispatch({ | ||||
| 		g_editor.dispatch({ | ||||
| 			changes: { | ||||
| 				from: 0, | ||||
| 				to: gEditor.state.doc.length, | ||||
| 				to: g_editor.state.doc.length, | ||||
| 				insert: formatted, | ||||
| 			}, | ||||
| 		}); | ||||
| @@ -1861,7 +1862,7 @@ window.addEventListener('load', function () { | ||||
| 	window.addEventListener('message', message, false); | ||||
| 	window.addEventListener('online', connectSocket); | ||||
| 	window.addEventListener('beforeunload', function () { | ||||
| 		gUnloading = true; | ||||
| 		g_unloading = true; | ||||
| 	}); | ||||
| 	document.getElementById('name').value = window.location.pathname; | ||||
| 	document | ||||
|   | ||||
		Reference in New Issue
	
	Block a user