Собственный VPN клиент на JavaScript. 8 часть — Electron компонент Setting
- суббота, 1 декабря 2018 г. в 00:18:43
context
│
│ index.js
│
└───client // все что относится к клиенту
│ creater-option.js
│ index.html
│ style.css
│
└───fonts
font1.woff2
font2.woff2
font3.woff2
font4.woff2
font5.woff2
font6.woff2
font7.woff2
const { BrowserWindow, ipcMain } = require('electron')
module.exports = class Notification {
constructor(parent) {
this.root = new BrowserWindow({
frame: false, // убираем рамку
transparent: true, // устанавливаем прозрачность
resizable: false, // запрещаем масштабирование
show: false, // запрещаем показывать окно после загрузки
height: 520,
width: 350,
center: true,
parent
})
// загружаем страницу
this.root.loadURL(`${__dirname}/client/index.html`)
// Обработчик сигнала SETTING_CLOSE (Скрывает окна)
ipcMain.on('SETTING_CLOSE', e => {
this.root.hide()
e.returnValue = 'ok'
})
}
onSave(cb) {
// Обработчик сигнала SETTING_SAVE сохраняет и скрывает окно
ipcMain.on('SETTING_SAVE', e => {
cb()
this.root.hide()
e.returnValue = 'ok'
})
}
ready() {
// Ожидаем пока окно полностью инициализируется
return new Promise(res => {
this.root.once('ready-to-show', res)
})
}
show() {
// Показываем окно
this.root.show()
}
hide() {
// Скрываем окно
this.root.hide()
}
get() {
// Запрашиваем настройки
return new Promise(resolve => {
this.root.webContents.send('SETTING_GET')
ipcMain.once('SETTING_DATA', (e, data) => {
resolve(JSON.parse(data))
})
})
}
}
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<link rel='stylesheet' type='text/css' href='style.css'>
</head>
<body>
<div class='body'>
<div class='settings'>
<div class='wrapper'>
<div>Количество сессий (от)</div>
<select name='NumVpnSessions'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Ping (до)</div>
<select name='Ping'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Всего подключений (от)</div>
<select name='TotalUsers'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Страна</div>
<select name='CountryLong'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Время работы (от)</div>
<select name='Uptime'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Скорость (от)</div>
<select name='Speed'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Общий трафик (от)</div>
<select name='TotalTraffic'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Качество соединения (от)</div>
<select name='Score'>
<option value='-'>---</option>
</select>
</div>
<div class='wrapper'>
<div>Расположен. увед.</div>
<select name='PositionNotify'>
<option value='BR'>Низ право</option>
<option value='TR'>Верх право</option>
<option value='BL'>Низ лево</option>
<option value='TL'>Верх лево</option>
</select>
</div>
<label for='auto-reconnect'>
<div class='wrapper'>
<div>Авто-переподключение к VPN</div>
<input type="checkbox" name='AutoReconnect' id='auto-reconnect' hidden>
<div class='new_check'></div>
</div>
</label>
<label for='auto-update'>
<div class='wrapper'>
<div>Авто-обновление серверов</div>
<input type="checkbox" name='AutoUpdate' id='auto-update' hidden>
<div class='new_check'></div>
</div>
</label>
<label for='permutation'>
<div class='wrapper'>
<div>Показывать по центру (при запуске)</div>
<input type="checkbox" name='Permutation' id='permutation' hidden>
<div class='new_check'></div>
</div>
</label>
<label for='start-hidden'>
<div class='wrapper'>
<div>Запускать свернутым</div>
<input type="checkbox" name='StartHidden' id='start-hidden' hidden>
<div class='new_check'></div>
</div>
</label>
</div>
<div class='btn-nav'>
<div class='btn save'>Сохранить</div>
<div class='btn close'>Закрыть</div>
</div>
</div>
<script>
const save = document.getElementsByClassName('save')[0]
, close = document.getElementsByClassName('close')[0]
, NumVpnSessions = document.getElementsByName('NumVpnSessions')[0]
, Ping = document.getElementsByName('Ping')[0]
, TotalUsers = document.getElementsByName('TotalUsers')[0]
, CountryLong = document.getElementsByName('CountryLong')[0]
, Uptime = document.getElementsByName('Uptime')[0]
, Speed = document.getElementsByName('Speed')[0]
, TotalTraffic = document.getElementsByName('TotalTraffic')[0]
, Score = document.getElementsByName('Score')[0]
, AutoReconnect = document.getElementsByName('AutoReconnect')[0]
, Permutation = document.getElementsByName('Permutation')[0]
, AutoUpdate = document.getElementsByName('AutoUpdate')[0]
, StartHidden = document.getElementsByName('StartHidden')[0]
, PositionNotify = document.getElementsByName('PositionNotify')[0]
, { ipcRenderer, shell } = require('electron')
// преобразуем настройки в строку JSON
const getSetting = () => JSON.stringify({
Ping: Ping.value || '-',
NumVpnSessions: NumVpnSessions.value || '-',
TotalUsers: TotalUsers.value || '-',
CountryLong: CountryLong.value || '-',
Uptime: Uptime.value || '-',
Speed: Speed.value || '-',
TotalTraffic: TotalTraffic.value || '-',
Score: Score.value || '-',
AutoReconnect: AutoReconnect.checked || false,
AutoUpdate: AutoUpdate.checked || false,
Permutation: Permutation.checked || false,
StartHidden: StartHidden.checked || false,
PositionNotify: PositionNotify.value || 'BR'
})
// сохранение настроек
save.addEventListener('click', () => {
// сохраняем в настройки в localStorage
localStorage.setItem('vpn_setting', getSetting())
// отсылам сигнал SETTING_SAVE в компонент
ipcRenderer.sendSync('SETTING_SAVE')
})
// закрытие окна
close.addEventListener('click', () => {
// отсылам сигнал SETTING_CLOSE в компонент
ipcRenderer.sendSync('SETTING_CLOSE')
})
</script>
<script src='creater-option.js'></script>
<script>
// устанавливаем всем полям значения из настроен
const vpn_setting = JSON.parse(localStorage.vpn_setting || "{}")
Ping.value = vpn_setting.Ping || '-'
NumVpnSessions.value = vpn_setting.NumVpnSessions || '-'
TotalUsers.value = vpn_setting.TotalUsers || '-'
CountryLong.value = vpn_setting.CountryLong || '-'
Uptime.value = vpn_setting.Uptime || '-'
Speed.value = vpn_setting.Speed || '-'
TotalTraffic.value = vpn_setting.TotalTraffic || '-'
Score.value = vpn_setting.Score || '-'
AutoReconnect.checked = vpn_setting.AutoReconnect || false
AutoUpdate.checked = vpn_setting.AutoUpdate || false
Permutation.checked = vpn_setting.Permutation || false
StartHidden.checked = vpn_setting.StartHidden || false
PositionNotify.value = vpn_setting.PositionNotify || 'BR'
// обработчик сигналов от компонента
ipcRenderer.on('SETTING_GET', (e, data) => {
// отсылам сигнал SETTING_DATA в компонент и с ним настройки
ipcRenderer.send('SETTING_DATA', getSetting())
})
</script>
</body>
</html>
// Активных сессий
NumVpnSessions.innerHTML += Array(100).fill(1).map((e, i) => {
if (i < 10) {
return `<option value='${i+1}'>${i+1}</option>`
}
return `<option value='${parseInt(i*1.5)}'>${parseInt(i*1.5)}</option>`
}).join('')
// Пинг
Ping.innerHTML += Array(150).fill(1).map((e, i) => {
if (i < 10) {
return `<option value='${i+1}'>${i+1}ms</option>`
}
return `<option value='${parseInt(i*1.5)}'>${parseInt(i*1.5)}ms</option>`
}).join('')
// Общее количество сессий
TotalUsers.innerHTML += Array(150).fill(1).map((e, i) => `<option value='${parseInt((i+1)*(i+1))}'>${parseInt((i+1)*(i+1))}</option>`).join('')
// Страна
const Contrys = ['Japan', 'Korea Republic of', 'United States', 'Thailand', 'Germany',
'New Zealand', 'Argentina', 'Poland', 'United Kingdom', 'Hong Kong',
'Viet Nam', 'Russian Federation', 'France', 'China', 'Singapore',
'Trinidad and Tobago', 'Moldova Republic of', 'Canada', 'Romania',
'Algeria', 'Venezuela', 'Indonesia', 'Brazil', 'Mexico', 'Cyprus',
'Iceland', 'Bangladesh', 'Morocco', 'Iraq', 'Ukraine', 'Iran',
'Turkey', 'Angola', 'El Salvador', 'Chile', 'Egypt', 'Spain',
'Netherlands', 'Colombia'
]
CountryLong.innerHTML += Contrys.map(e => `<option value='${e}'>${e}</option>`).join('')
// Время работы
Uptime.innerHTML += Array(79).fill(1).map((e, i) => {
if (i < 15) {
return `<option value='${(i+1)*60*1000}'>${i+1} мин.</option>`
}
if (i < 18) {
return `<option value='${(i-13)*15*60*1000}'>${(i-13)*15} мин.</option>`
}
if (i < 40) {
return `<option value='${(i-16)*60*60*1000}'>${(i-16)} час.</option>`
}
if (i < 69) {
return `<option value='${(i-38)*24*60*60*1000}'>${(i-38)} ден.</option>`
}
if (i < 79) {
return `<option value='${(i-67)*30*24*60*60*1000}'>${(i-67)} мес.</option>`
}
}).join('')
// Скорость
Speed.innerHTML += Array(100).fill(1).map((e, i) => {
if (i < 5) {
return `<option value='${parseInt(1048576*((i+1)/100))}'>${(i+1)/100} Mbit</option>`
}
if (i < 9) {
return `<option value='${parseInt(1048576*((i+1)/10))}'>${(i+1)/10} Mbit</option>`
}
if (i < 19) {
return `<option value='${parseInt(1048576*(i-8))}'>${(i-8)} Mbit</option>`
}
return `<option value='${parseInt(1048576*((i-18)*i))}'>${(i-18)*i} Mbit</option>`
}).join('')
// Общий трафик
TotalTraffic.innerHTML += Array(150).fill(1).map((e, i) => {
if (i < 10) {
return `<option value='${parseInt(1048576*(i+1))}'>${i+1} Mbit</option>`
}
if (i < 20) {
return `<option value='${parseInt(1048576*((i+1)*10))}'>${(i+1)*10} Mbit</option>`
}
if (i < 50) {
return `<option value='${parseInt(1048576*((i+1)*100))}'>${(i+1)*100} Mbit</option>`
}
if (i < 60) {
return `<option value='${parseInt(1073741824*((i-45)*1))}'>${(i-45)*1} Gbit</option>`
}
if (i < 70) {
return `<option value='${parseInt(1073741824*((i-45)*10))}'>${(i-45)*10} Gbit</option>`
}
if (i < 150) {
return `<option value='${parseInt(1073741824*((i-45)*100))}'>${(i-45)*100} Gbit</option>`
}
}).join('')
// Качество соединения
Score.innerHTML += Array(100).fill(1).map((e, i) => `<option value='${(i+25)*(i+25)*(i+25)}'>${i+1}%</option>`).reverse().join('')
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font1.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font2.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font3.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font4.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font5.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font6.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font7.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
* {
font-family: 'Source Sans Pro', sans-serif;
padding: 0;
margin: 0;
overflow: hidden;
background: rgba(0, 0, 0, 0);
color: #eee;
}
.body {
font-size: 14px;
width: 345px;
border-radius: 10px;
background: rgba(36, 39, 39, 0.9);
padding: 20px 0px 20px 0px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 480px;
}
.settings {
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
}
.wrapper {
display: flex;
align-items: center;
justify-content: space-between;
width: 300px
}
select {
outline: none;
border: none;
background: rgba(51, 55, 55, 0.9);
border-radius: 6px;
padding: 4px 10px 4px 10px;
cursor: pointer;
}
select:hover {
background: rgba(53, 57, 57, 0.9);
}
option {
background: rgba(56, 60, 60, 0.9);
}
.btn-nav {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: 30px;
}
.btn {
cursor: pointer;
margin-top: 10px;
padding: 4px 10px 4px 10px;
border-radius: 6px;
user-select: none;
margin: 0px 10px 0px 10px;
}
.btn:hover {
color: #ccc
}
.button {
margin-top: 10px;
}
.new_check {
cursor: pointer;
width: 20px;
height: 20px;
border-radius: 100%;
margin-right: 30px;
background: rgba(51, 55, 55, 0.9);
}
#auto-reconnect:checked + .new_check {
background: radial-gradient(rgba(244, 244, 244, 0.9) 30%, rgba(51, 55, 55, 0.9) 29%);
}
#auto-update:checked + .new_check {
background: radial-gradient(rgba(244, 244, 244, 0.9) 30%, rgba(51, 55, 55, 0.9) 29%);
}
#permutation:checked + .new_check {
background: radial-gradient(rgba(244, 244, 244, 0.9) 30%, rgba(51, 55, 55, 0.9) 29%);
}
#start-hidden:checked + .new_check {
background: radial-gradient(rgba(244, 244, 244, 0.9) 30%, rgba(51, 55, 55, 0.9) 29%);
}
const { app } = require('electron')
, SETTING = require('./../../app/components/setting')
app.on('ready', async() => {
const Setting = new SETTING()
// Только после того как окно инициализируются программа продолжит исполнятся
await Setting.ready()
// Показывает окно
Setting.show()
// Обработчик сохранения
Setting.onSave(async () => {
// Запрашиваем настройки
const vpn_setting = await Setting.get()
console.log(vpn_setting)
})
})