diff --git a/config.js b/config.js index d466cb7..06bc9e0 100644 --- a/config.js +++ b/config.js @@ -1,5 +1,5 @@ -'use strict'; -const Store = require('electron-store'); +'use strict' +const Store = require('electron-store') module.exports = new Store({ defaults: { @@ -24,6 +24,6 @@ module.exports = new Store({ plugin_companion: true, switch_mdns: true, switch_settings: true, - switch_monitor: true - } -}); + switch_monitor: true, + }, +}) diff --git a/help.md b/help.md index c017a6d..34729a1 100644 --- a/help.md +++ b/help.md @@ -1,21 +1,22 @@ # Presentation Bridge Client -This software is designed to work with your presentation lyrics software, accessing slide data in real time and then performing custom actions. It integrates with PresentationBridge, a free server program that allows you to send lyric data to end devices such as phones, tablets, and tv sticks through the built in web browser on those devices. +This software is designed to work with your presentation lyrics software, accessing slide data in real time and then performing custom actions. It integrates with PresentationBridge, a free server program that allows you to send lyric data to end devices such as phones, tablets, and tv sticks through the built in web browser on those devices. -The Settings window is used to configure the software for ProPresenter, PresentationBridge, midi-relay, Vista, Companion and HTTP. +The Settings window is used to configure the software for ProPresenter, PresentationBridge, midi-relay, Vista, Companion and HTTP. -Text on slides is sent to the bridge for display on devices that are connected. +Text on slides is sent to the bridge for display on devices that are connected. Text will be converted to upper case if the 'Upper Case' switch is set. This is used to match text display if a ProPresenter upper case theme is being used. -Images of the slides will be sent if the 'Send Images' switch in settings is on. +Images of the slides will be sent if the 'Send Images' switch in settings is on. The stage display shows the slide text and notes for both the current slide and next. In addition, using the notes section of a slide, this Client can also send commands using midi-relay, HTTP and Companion: -* Send MIDI voice messages, using the free program [midi-relay](http://github.com/josephdadams/midi-relay) -* Make HTTP Requests (GET and POST, with JSON data if needed) -* Press Companion Buttons using the TCP protocol + +- Send MIDI voice messages, using the free program [midi-relay](http://github.com/josephdadams/midi-relay) +- Make HTTP Requests (GET and POST, with JSON data if needed) +- Press Companion Buttons using the TCP protocol In general, the format is as follows: @@ -23,152 +24,157 @@ In general, the format is as follows: Each parameter is separated by a comma and a command should be terminated with a semicolon if there are other text notes or commands on the same notes page. A single command on the notes page does not need the terminating semicolon (for backwards compatibility). -The program works by listening to the "current slide notes" data within ProPresenter. If the text contains valid commands then these will be processed. Other text is sent to the stagedisplay bridge. +The program works by listening to the "current slide notes" data within ProPresenter. If the text contains valid commands then these will be processed. Other text is sent to the stagedisplay bridge. ## Sending MIDI Relay Messages ### Supported MIDI Relay Types -* Note On - - Format: `noteon:[channel],[note],[velocity];` - - * `channel` should be a integer between 0 and 15. - * `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. - * `velocity` should be a integer between 1 and 127. A value of 0 is considered a Note Off message. - Example: `noteon:0,55,100;` - -* Note Off - - Format: `noteoff:[channel],[note],[velocity];` - - * `channel` should be a integer between 0 and 15. - * `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. - * `velocity` should be 0. +- Note On + + Format: `noteon:[channel],[note],[velocity];` + + - `channel` should be a integer between 0 and 15. + - `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. + - `velocity` should be a integer between 1 and 127. A value of 0 is considered a Note Off message. + + Example: `noteon:0,55,100;` + +- Note Off + + Format: `noteoff:[channel],[note],[velocity];` + + - `channel` should be a integer between 0 and 15. + - `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. + - `velocity` should be 0. + + Example: `noteoff:0,55,100;` + +- Polyphonic Aftertouch + + Format: `aftertouch:[channel],[note],[value];` - Example: `noteoff:0,55,100;` + - `channel` should be a integer between 0 and 15. + - `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. + - `velocity` should be a integer between 0 and 127. -* Polyphonic Aftertouch + Example: `aftertouch:0,55,100;` - Format: `aftertouch:[channel],[note],[value];` - - * `channel` should be a integer between 0 and 15. - * `note` should be an integer of the MIDI Number value that represents the note, between 0 and 127. - * `velocity` should be a integer between 0 and 127. +- CC (Control Change) - Example: `aftertouch:0,55,100;` + Format: `cc:[channel],[controller],[value];` -* CC (Control Change) + - `channel` should be a integer between 0 and 15. + - `controller` should be a integer between 0 and 127. + - `value` should be a integer between 0 and 127. - Format: `cc:[channel],[controller],[value];` - - * `channel` should be a integer between 0 and 15. - * `controller` should be a integer between 0 and 127. - * `value` should be a integer between 0 and 127. + Example: `cc:0,32,100;` - Example: `cc:0,32,100;` - -* PC (Program Change) +- PC (Program Change) - Format: `pc:[channel],[value];` + Format: `pc:[channel],[value];` - * `channel` should be a integer between 0 and 15. - * `value` should be a integer between 0 and 127. + - `channel` should be a integer between 0 and 15. + - `value` should be a integer between 0 and 127. - Example: `pc:0,100;` + Example: `pc:0,100;` -* Channel Pressure / Aftertouch +- Channel Pressure / Aftertouch - Format: `pressure:[channel],[value];` - - * `channel` should be a integer between 0 and 15. - * `value` should be a integer between 0 and 127. + Format: `pressure:[channel],[value];` - Example: `pressure:0,100;` + - `channel` should be a integer between 0 and 15. + - `value` should be a integer between 0 and 127. -* Pitch Bend / Pitch Wheel + Example: `pressure:0,100;` - Format: `pitchbend:[channel],[value];` - - * `channel` should be a integer between 0 and 15. - * `value` should be a integer between 0 and 16,383. +- Pitch Bend / Pitch Wheel - Example: `pitchbend:0,100;` + Format: `pitchbend:[channel],[value];` -* MSC (MIDI Show Control) + - `channel` should be a integer between 0 and 15. + - `value` should be a integer between 0 and 16,383. - Format: `msc:[device id],[command format],[command],[cue],[cuelist],[cuepath];` + Example: `pitchbend:0,100;` - * `deviceid` should be an integer between 0 and 111. It can also be a string 'g1' through 'g15' to represent groups, or it can be `all`. +- MSC (MIDI Show Control) - * `commandformat` should be a string with one of the following values: - * lighting.general - * sound.general - * machinery.general - * video.general - * projection.general - * processcontrol.general - * pyro.general - * all - * Any other value for _commandformat_ will default to `all`. - - * `command` should be a string with one of the following values: - * go - * stop - * gojam - * gooff - * resume - * timedgo - * load - * set - * fire - * alloff - * restore - * reset - * opencuelist - * closecuelist - * startclock - * stopclock + Format: `msc:[device id],[command format],[command],[cue],[cuelist],[cuepath];` - * Values for `cue`, `cuelist`, and `cuepath` are all optional strings. If you don't want to include them, just include the `,` delimiter. - - Example: `msc:0,lighting.general,go,1,12,,;` + - `deviceid` should be an integer between 0 and 111. It can also be a string 'g1' through 'g15' to represent groups, or it can be `all`. -* Sysex MIDI Message + - `commandformat` should be a string with one of the following values: - Format: `sysex:[message];` - - * `message` should contain the actual MIDI message in bytes. Bytes can be either in hexadecimal or decimal, separated by commas. - * A response of `{result: 'sysex-sent-successfully'}` indicates the SysEx MIDI message was successfully sent. + - lighting.general + - sound.general + - machinery.general + - video.general + - projection.general + - processcontrol.general + - pyro.general + - all + - Any other value for _commandformat_ will default to `all`. + + - `command` should be a string with one of the following values: + + - go + - stop + - gojam + - gooff + - resume + - timedgo + - load + - set + - fire + - alloff + - restore + - reset + - opencuelist + - closecuelist + - startclock + - stopclock + + - Values for `cue`, `cuelist`, and `cuepath` are all optional strings. If you don't want to include them, just include the `,` delimiter. + + Example: `msc:0,lighting.general,go,1,12,,;` + +- Sysex MIDI Message + + Format: `sysex:[message];` + + - `message` should contain the actual MIDI message in bytes. Bytes can be either in hexadecimal or decimal, separated by commas. + - A response of `{result: 'sysex-sent-successfully'}` indicates the SysEx MIDI message was successfully sent. + + Example: `sysex:0xF0,0x41,0x10,0x00,0x00,0x00,0x20,0x12,0x71,0x00,0x08,0x00,0x07,0xF7;` - Example: `sysex:0xF0,0x41,0x10,0x00,0x00,0x00,0x20,0x12,0x71,0x00,0x08,0x00,0x07,0xF7;` - All values for `channel` are zero-based. (e.g., Channel of `1` should be sent as `0`.) ## Additional MIDI Notations + In addition to the primary MIDI voice message notations, the following notations are also available: -* Chroma-Q Vista Lighting MSC Message (the Vista console must be running midi-relay) +- Chroma-Q Vista Lighting MSC Message (the Vista console must be running midi-relay) - Format: `vista:[cue list],[cue];` + Format: `vista:[cue list],[cue];` - * `cue list`: The cue list you want to control - * `cue`: The cue to play + - `cue list`: The cue list you want to control + - `cue`: The cue to play - Example: `vista:1,2.5;` : This would run Cue 2.5 on Cuelist 1. This assumes a `device id` of `0`, `command format` is `lighting.general`, and the `command` is `go`. + Example: `vista:1,2.5;` : This would run Cue 2.5 on Cuelist 1. This assumes a `device id` of `0`, `command format` is `lighting.general`, and the `command` is `go`. ## HTTP Requests + HTTP Requests to perform actions or send data can also be easily performed. Format: `http:[url];` -* `url`: The URL to perform an HTTP GET request +- `url`: The URL to perform an HTTP GET request You can also use the following notations to perform specific HTTP request types: -* `http:get,[url];` -* `http:post,[url];` -* `http:post,[url],[json];` +- `http:get,[url];` +- `http:post,[url];` +- `http:post,[url],[json];` Example: `http:get,http://www.techministry.blog;` : This would perform an HTTP GET request to the URL, `http://www.techministry.blog`. @@ -176,8 +182,8 @@ Example: `http:get,http://www.techministry.blog;` : This would perform an HTTP G Format: `companion:[bank],[button];` or `cbp:[bank],[button];` -* `bank`: The page in Companion where the button is -* `button`: The button on the page that you wish to press +- `bank`: The page in Companion where the button is +- `button`: The button on the page that you wish to press Example: `companion:1,2;` : This would press Button 2 on Page 1. @@ -185,6 +191,6 @@ Example: `companion:1,2;` : This would press Button 2 on Page 1. Format: `logo:[switch];` -* `switch`: Either `on` or `off` +- `switch`: Either `on` or `off` Example: `logo:on;` : To turn the logo on. diff --git a/index.css b/index.css index 1a72dc1..171a0c8 100644 --- a/index.css +++ b/index.css @@ -7,18 +7,8 @@ body { /* Use OS default fonts */ body { - font-family: -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Roboto, - Oxygen-Sans, - Ubuntu, - Cantarell, - 'Helvetica Neue', - sans-serif, - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol'; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; text-rendering: optimizeLegibility; font-feature-settings: 'liga', 'clig', 'kern'; } @@ -34,19 +24,24 @@ header h1 { padding: 0; opacity: 0.7; } -header h2, h3 { +header h2, +h3 { width: 50%; display: inline; } -header h2 { font-size: 30px} -header h3 { font-size: 15px} +header h2 { + font-size: 30px; +} +header h3 { + font-size: 15px; +} .vertical-space { margin-top: 3px; } .horizontal-line { - border-top: 3px solid #000000; + border-top: 3px solid #000000; margin-bottom: 1px; } diff --git a/index.html b/index.html index 4e96ac9..5bb0e46 100644 --- a/index.html +++ b/index.html @@ -1,389 +1,412 @@ - + - + PresentationBridge Client - - - - - + + + + + @@ -391,293 +414,388 @@

PresentationBridge Client

-

Created by techministry.blog

-
+

Created by techministry.blog

+

+
-
-
-
- ProPresenter: +
+
+
+ ProPresenter:
-
-
+
+
-
-
- +
+
+
-
+
- +
-
+
-
- +
+
-
-
+
+
- +
-
-
+
+
-
-
+
+
- +
-
+
- +
-
-
-
+
+
+
Presentation Bridge:
-
-
+
+
-
-
- +
+
+
-
+
- +
-
+
-
- +
+
-
-
+
+
- +
-
+
- +
-
-
-
-
- - +
+
+
+
+ +
-
-
+
+
-
-
+
+
-
- +
+
-
+
- +
-
+
-
- +
+
-
-
-
+
+
+
- + -
+
-
-
+
+
-
- +
+
-
+
- +
-
+
-
- +
+
-
-
-
+
+
+
- + -
+
-
+
- + -
+
-
-
-
-
+
+
+
- +
-
+
-
- +
+
+
-
-
-
-
-
- -
-
-
- +
+
+
+ +
+
+
+ +
-
-
Start-up mdns scan and window visibility -
-
-
- - +
+ Start-up mdns scan and window visibility +
+
+
+ + +
-
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
+
+ +
-
- -
-
-
-
diff --git a/index.js b/index.js index a531aa2..cbdb74d 100644 --- a/index.js +++ b/index.js @@ -1,162 +1,161 @@ -'use strict'; -const path = require('path'); -const {app, BrowserWindow, Menu, Tray, ipcMain, nativeImage, dialog } = require('electron'); -const {autoUpdater} = require('electron-updater'); -const {is} = require('electron-util'); -const unhandled = require('electron-unhandled'); -const debug = require('electron-debug'); -const config = require('./config'); -const WebSocket = require('ws'); +'use strict' +const path = require('path') +const { app, BrowserWindow, Menu, Tray, ipcMain, nativeImage, dialog } = require('electron') +const { autoUpdater } = require('electron-updater') +const { is } = require('electron-util') +const unhandled = require('electron-unhandled') +const debug = require('electron-debug') +const config = require('./config') +const WebSocket = require('ws') //mdns variables -const mdns = require('mdns-js'); -var mdns_browse_window_timer = undefined; -var mdns_browser_propresenter = undefined; //mdns browser variable -var mdns_browser_midirelay = undefined; //mdns browser variable -var mdns_propresenter_hosts = []; //global array of found hosts through mdns -var mdns_midirelay_hosts = []; //global array of found hosts through mdns +const mdns = require('mdns-js') +var mdns_browse_window_timer = undefined +var mdns_browser_propresenter = undefined //mdns browser variable +var mdns_browser_midirelay = undefined //mdns browser variable +var mdns_propresenter_hosts = [] //global array of found hosts through mdns +var mdns_midirelay_hosts = [] //global array of found hosts through mdns //ProPresenter variables -var propresenter_socket = undefined; -var propresenter_status = 'disconnected'; -var propresenter_cs = ''; -var propresenter_csn = ''; -var propresenter_csn_commands = []; -var propresenter_ns = ''; -var propresenter_nsn = ''; -var propresenter_nsn_commands = []; - -const axios = require('axios'); -const socketio = require('socket.io-client'); -const net = require('net'); +var propresenter_socket = undefined +var propresenter_status = 'disconnected' +var propresenter_cs = '' +var propresenter_csn = '' +var propresenter_csn_commands = [] +var propresenter_ns = '' +var propresenter_nsn = '' +var propresenter_nsn_commands = [] + +const axios = require('axios') +const socketio = require('socket.io-client') +const net = require('net') //PresentationBridge variables -let bridgeConnected = false; -let bridgeIO = null; -var presentationbridge_status = 'disconnected'; +let bridgeConnected = false +let bridgeIO = null +var presentationbridge_status = 'disconnected' -var commands_on = true; -var lyrics_on = true; +var commands_on = true +var lyrics_on = true //Companion socket -let companionClient = undefined; +let companionClient = undefined -unhandled(); +unhandled() //debug(); var trayMenuItems = [ { label: 'ProPresenter: Disconnected', - enabled: false + enabled: false, }, { label: 'Presentation Bridge: Disconnected', - enabled: false + enabled: false, }, { label: 'Settings', click: function () { - openSettings(); - } + openSettings() + }, }, { label: 'Go To Logo', click: function () { - presentationbridge_gotologo(); - } + presentationbridge_gotologo() + }, }, { label: 'Turn Off Commands', click: function () { - turnOffCommands(); - } + turnOffCommands() + }, }, { label: 'Turn Off Cloud Lyrics', click: function () { - turnOffLyrics(); - } + turnOffLyrics() + }, }, { label: 'Monitor', click: function () { - openMonitor(); - } + openMonitor() + }, }, { - type: 'separator' + type: 'separator', }, { label: 'Check for Updates', click: function () { - checkForUpdates(); + checkForUpdates() }, }, { label: 'Quit', click: function () { - app.quit(); - } - } -]; -var tray = null; + app.quit() + }, + }, +] +var tray = null // Note: Must match `build.appId` in package.json -app.setAppUserModelId('com.techministry.presentationbridgeclient'); +app.setAppUserModelId('com.techministry.presentationbridgeclient') // Prevent windows from being garbage collected -let settingsWindow; -let monitorWindow; +let settingsWindow +let monitorWindow // Prevent multiple instances of the app if (!app.requestSingleInstanceLock()) { - app.quit(); + app.quit() } app.on('second-instance', () => { if (settingsWindow) { if (settingsWindow.isMinimized()) { - settingsWindow.restore(); + settingsWindow.restore() } - settingsWindow.show(); + settingsWindow.show() } if (monitorWindow) { if (monitorWindow.isMinimized()) { - monitorWindow.restore(); + monitorWindow.restore() } - monitorWindow.show(); + monitorWindow.show() } -}); +}) app.on('window-all-closed', () => { /* Don't quit just because windows are closed as Tray will still be running */ -}); +}) app.on('quit', () => { if (companionClient !== undefined) { companionClient.destroy() } - clearLyrics(); -}); + clearLyrics() +}) app.on('activate', async () => { if (!settingsWindow) { - settingsWindow = await createsettingsWindow(); + settingsWindow = await createsettingsWindow() } if (!monitorWindow) { - monitorWindow = await createmonitorWindow(); + monitorWindow = await createmonitorWindow() } - if (process.platform === "darwin") - { - app.dock.hide(); // Keep it out of the dock + if (process.platform === 'darwin') { + app.dock.hide() // Keep it out of the dock } -}); +}) const createsettingsWindow = async () => { const win = new BrowserWindow({ @@ -168,38 +167,37 @@ const createsettingsWindow = async () => { webPreferences: { contextIsolation: false, nodeIntegration: true, - enableRemoteModule: true - } - }); + enableRemoteModule: true, + }, + }) win.on('ready-to-show', () => { if (config.get('switch_settings')) { - win.show(); - SendStatusMessage(); + win.show() + SendStatusMessage() } - }); + }) win.on('closed', () => { // Dereference the window // For multiple windows store them in an array settingsWindow = undefined - }); + }) - await win.loadFile(path.join(__dirname, 'index.html')); + await win.loadFile(path.join(__dirname, 'index.html')) - return win; -}; + return win +} const openSettings = async () => { if (settingsWindow !== undefined) { if (settingsWindow.isMinimized()) { - settingsWindow.restore(); + settingsWindow.restore() } - settingsWindow.show(); - } - else { - settingsWindow = await createsettingsWindow(); + settingsWindow.show() + } else { + settingsWindow = await createsettingsWindow() } } @@ -213,86 +211,89 @@ const createmonitorWindow = async () => { webPreferences: { contextIsolation: false, nodeIntegration: true, - enableRemoteModule: true - } - }); + enableRemoteModule: true, + }, + }) win2.on('ready-to-show', () => { if (config.get('switch_monitor')) { - win2.show(); + win2.show() } - }); + }) win2.on('closed', () => { // Dereference the window // For multiple windows store them in an array - monitorWindow = undefined; - }); + monitorWindow = undefined + }) - await win2.loadFile(path.join(__dirname, 'monitor.html')); + await win2.loadFile(path.join(__dirname, 'monitor.html')) - return win2; -}; + return win2 +} const openMonitor = async () => { - if (monitorWindow !== undefined) { if (monitorWindow.isMinimized()) { - monitorWindow.restore(); + monitorWindow.restore() } - monitorWindow.show(); - } - else { - monitorWindow = await createmonitorWindow(); - monitorWindow.show(); + monitorWindow.show() + } else { + monitorWindow = await createmonitorWindow() + monitorWindow.show() } } function checkForUpdates() { - autoUpdater.autoDownload = false; - autoUpdater.autoInstallOnAppQuit = false; - autoUpdater.checkForUpdates(); - autoUpdater.on("update-available", () => { + autoUpdater.autoDownload = false + autoUpdater.autoInstallOnAppQuit = false + autoUpdater.checkForUpdates() + autoUpdater.on('update-available', () => { if (settingsWindow) { - settingsWindow.show(); - - dialog.showMessageBox(settingsWindow, { - title: "Update Available", - message: "There's an update available for PresentationBridge Client. Do you want to download and install it?", - buttons: ["Update", "Cancel"], - }).then((v) => { - if (v.response == 0) { - dialog.showMessageBox(settingsWindow, { - title: "Downloading update", - message: "The update is being downloaded in the background. Once finished, you will be prompted to save your work and restart PresentationBridge Client." - }); - autoUpdater.downloadUpdate(); + settingsWindow.show() + + dialog + .showMessageBox(settingsWindow, { + title: 'Update Available', + message: "There's an update available for PresentationBridge Client. Do you want to download and install it?", + buttons: ['Update', 'Cancel'], + }) + .then((v) => { + if (v.response == 0) { + dialog.showMessageBox(settingsWindow, { + title: 'Downloading update', + message: + 'The update is being downloaded in the background. Once finished, you will be prompted to save your work and restart PresentationBridge Client.', + }) + autoUpdater.downloadUpdate() + } + }) + } + }) + autoUpdater.on('update-downloaded', () => { + dialog + .showMessageBox(null, { + title: 'Update downloaded', + message: 'The update has been downloaded. Save your work and then press the Update button.', + buttons: ['Update'], + }) + .then((r) => { + if (r.response == 0) { + autoUpdater.quitAndInstall() } - }); - } - }); - autoUpdater.on("update-downloaded", () => { - dialog.showMessageBox(null, { - title: "Update downloaded", - message: "The update has been downloaded. Save your work and then press the Update button.", - buttons: ["Update"], - }).then((r) => { - if (r.response == 0) { - autoUpdater.quitAndInstall(); - } - }); - }); + }) + }) } -(async () => { - await app.whenReady(); +;(async () => { + await app.whenReady() - settingsWindow = await createsettingsWindow(); - monitorWindow = await createmonitorWindow(); - - tray = new Tray(path.join(__dirname,'Bridge-icon.png')); - buildTray(); + settingsWindow = await createsettingsWindow() + monitorWindow = await createmonitorWindow() + + tray = new Tray(path.join(__dirname, 'Bridge-icon.png')) + buildTray() /*if (!is.development) { const FOUR_HOURS = 1000 * 60 * 60 * 4; @@ -303,386 +304,388 @@ function checkForUpdates() { }*/ if (config.get('switch_mdns')) { - findHosts(); + findHosts() } if (config.get('propresenterIP') !== '') { - propresenter_connect(); + propresenter_connect() } if (config.get('presentationbridgeHost') !== '') { - presentationbridge_connect(); + presentationbridge_connect() } -})(); +})() function buildTray() { - let trayContextMenu = Menu.buildFromTemplate(trayMenuItems); - tray.setToolTip('Presentation Bridge Client'); - tray.setContextMenu(trayContextMenu); + let trayContextMenu = Menu.buildFromTemplate(trayMenuItems) + tray.setToolTip('Presentation Bridge Client') + tray.setContextMenu(trayContextMenu) } -function mdns_close_browsers(){ - mdns_browser_propresenter.stop(); - mdns_browser_midirelay.stop(); +function mdns_close_browsers() { + mdns_browser_propresenter.stop() + mdns_browser_midirelay.stop() - mdns_browser_propresenter = undefined; - mdns_browser_midirelay = undefined; + mdns_browser_propresenter = undefined + mdns_browser_midirelay = undefined } function findHosts() { - - mdns_browser_propresenter = mdns.createBrowser(mdns.tcp('pro7stagedsply')); - mdns_browser_midirelay = mdns.createBrowser(mdns.tcp('midi-relay')); + mdns_browser_propresenter = mdns.createBrowser(mdns.tcp('pro7stagedsply')) + mdns_browser_midirelay = mdns.createBrowser(mdns.tcp('midi-relay')) mdns_browse_window_timer = setTimeout(mdns_close_browsers, 5000) // stop browser after a discovery period to protect from low level memory leak error that can occur in rare circumstances mdns_browser_propresenter.on('ready', function onReady() { - mdns_browser_propresenter.discover(); - }); + mdns_browser_propresenter.discover() + }) mdns_browser_propresenter.on('update', function onUpdate(data) { //console.log(data); for (let i = 0; i < data.type.length; i++) { if (data.type[i].name.indexOf('pro7stagedsply') > -1) { - mdns_propresenter_addhost(data.host, data.addresses[0], data.port, data.fullname); - } + mdns_propresenter_addhost(data.host, data.addresses[0], data.port, data.fullname) + } } - }); + }) mdns_browser_midirelay.on('ready', function onReady() { - mdns_browser_midirelay.discover(); - }); + mdns_browser_midirelay.discover() + }) mdns_browser_midirelay.on('update', function onUpdate(data) { //console.log(data); for (let i = 0; i < data.type.length; i++) { if (data.type[i].name.indexOf('midi-relay') > -1) { - mdns_midirelay_addhost(data.host, data.addresses[0], data.port); - } + mdns_midirelay_addhost(data.host, data.addresses[0], data.port) + } } - }); + }) } function mdns_propresenter_addhost(host, ip, port, name) { // first check to see if it's already in the list, and don't add it if so - let isFound = false; + let isFound = false for (let i = 0; i < mdns_propresenter_hosts.length; i++) { if (mdns_propresenter_hosts[i].ip === ip) { - isFound = true; - mdns_propresenter_hosts[i].port = port; - mdns_propresenter_hosts[i].name = name ? name.substring(0, name.indexOf('.')) : ''; - break; + isFound = true + mdns_propresenter_hosts[i].port = port + mdns_propresenter_hosts[i].name = name ? name.substring(0, name.indexOf('.')) : '' + break } } if (!isFound) { - let hostObj = {}; - hostObj.name = host; - hostObj.ip = ip; - hostObj.port = port; - hostObj.name = name ? name.substring(0, name.indexOf('.')) : ''; - mdns_propresenter_hosts.push(hostObj); + let hostObj = {} + hostObj.name = host + hostObj.ip = ip + hostObj.port = port + hostObj.name = name ? name.substring(0, name.indexOf('.')) : '' + mdns_propresenter_hosts.push(hostObj) } if (settingsWindow) { - settingsWindow.webContents.send('mdns_propresenter_hosts', mdns_propresenter_hosts); + settingsWindow.webContents.send('mdns_propresenter_hosts', mdns_propresenter_hosts) } } function mdns_midirelay_addhost(host, ip, port) { // first check to see if it's already in the list, and don't add it if so - let isFound = false; + let isFound = false for (let i = 0; i < mdns_midirelay_hosts.length; i++) { if (mdns_midirelay_hosts[i].ip === ip) { - isFound = true; - mdns_midirelay_hosts[i].port = port; - break; + isFound = true + mdns_midirelay_hosts[i].port = port + break } } if (!isFound) { - let hostObj = {}; - hostObj.name = host; - hostObj.ip = ip; - hostObj.port = port; - mdns_midirelay_hosts.push(hostObj); + let hostObj = {} + hostObj.name = host + hostObj.ip = ip + hostObj.port = port + mdns_midirelay_hosts.push(hostObj) } if (settingsWindow) { - settingsWindow.webContents.send('mdns_midirelay_hosts', mdns_midirelay_hosts); + settingsWindow.webContents.send('mdns_midirelay_hosts', mdns_midirelay_hosts) } - console.log('mdns hosts', mdns_midirelay_hosts); + console.log('mdns hosts', mdns_midirelay_hosts) } - function propresenter_connect() { - let ip = config.get('propresenterIP'); - let port = config.get('propresenterPort'); - let password = config.get('propresenterPassword'); + let ip = config.get('propresenterIP') + let port = config.get('propresenterPort') + let password = config.get('propresenterPassword') - console.log('Opening ProPresenter Socket Connection: ' + ip + ':' + port); + console.log('Opening ProPresenter Socket Connection: ' + ip + ':' + port) - propresenter_status = 'connecting'; - SendStatusMessage(); - trayMenuItems[0].label = 'Connecting: ' + ip + ':' + port; - buildTray(); + propresenter_status = 'connecting' + SendStatusMessage() + trayMenuItems[0].label = 'Connecting: ' + ip + ':' + port + buildTray() - propresenter_socket = new WebSocket('ws://' + ip + ':' + port + '/stagedisplay'); + propresenter_socket = new WebSocket('ws://' + ip + ':' + port + '/stagedisplay') propresenter_socket.on('open', function open() { - console.log('ProPresenter Connection Opened.'); - propresenter_status = 'open'; - SendStatusMessage(); - - trayMenuItems[0].label = 'Connected: ' + ip + ':' + port; - buildTray(); - - propresenter_socket.send(JSON.stringify({ - pwd: password, - ptl: 610, - acn: "ath" - })); - }); + console.log('ProPresenter Connection Opened.') + propresenter_status = 'open' + SendStatusMessage() + + trayMenuItems[0].label = 'Connected: ' + ip + ':' + port + buildTray() + + propresenter_socket.send( + JSON.stringify({ + pwd: password, + ptl: 610, + acn: 'ath', + }) + ) + }) - propresenter_socket.on('message', function(message) { + propresenter_socket.on('message', function (message) { // Handle the stage display message received from ProPresenter - handleStageDisplayMessage(message); - }); + handleStageDisplayMessage(message) + }) propresenter_socket.on('error', function (err) { try { if (err.errno !== undefined) { if (err.errno.indexOf('ECONNREFUSED') > -1) { - propresenter_status = 'econnrefused'; - SendStatusMessage(); - trayMenuItems[0].label = 'Connection Refused: ' + ip + ':' + port; - buildTray(); - } - else if (err.errno.indexOf('ETIMEDOUT') > -1) { - propresenter_status = 'etimedout'; - SendStatusMessage(); - trayMenuItems[0].label = 'Connection Timed Out: ' + ip + ':' + port; - buildTray(); + propresenter_status = 'econnrefused' + SendStatusMessage() + trayMenuItems[0].label = 'Connection Refused: ' + ip + ':' + port + buildTray() + } else if (err.errno.indexOf('ETIMEDOUT') > -1) { + propresenter_status = 'etimedout' + SendStatusMessage() + trayMenuItems[0].label = 'Connection Timed Out: ' + ip + ':' + port + buildTray() } } - } - catch(error) { - - } - }); + } catch (error) {} + }) - propresenter_socket.on('close', function(code, reason) { - console.log('ProPresenter disconnected.'); + propresenter_socket.on('close', function (code, reason) { + console.log('ProPresenter disconnected.') //console.log('Current State: ' + propresenter_status); if (propresenter_status !== 'force_disconnected') { - setTimeout(propresenter_reconnect, 5000); //attempt to reeconnect until the user forces it to stop + setTimeout(propresenter_reconnect, 5000) //attempt to reeconnect until the user forces it to stop } - SendStatusMessage(); - }); + SendStatusMessage() + }) } function propresenter_disconnect(reconnect) { - console.log('Closing ProPresenter Connection.'); - propresenter_status = 'force_disconnected'; - trayMenuItems[0].label = 'Disconnected: ' + config.get('propresenterIP') + ':' + config.get('propresenterPort'); - buildTray(); - SendStatusMessage(); + console.log('Closing ProPresenter Connection.') + propresenter_status = 'force_disconnected' + trayMenuItems[0].label = 'Disconnected: ' + config.get('propresenterIP') + ':' + config.get('propresenterPort') + buildTray() + SendStatusMessage() if (reconnect) { - propresenter_reconnect(); + propresenter_reconnect() } } function propresenter_reconnect() { - console.log('Attempting to reconnect to ProPresenter...'); - propresenter_status = 'reconnecting'; - SendStatusMessage(); - propresenter_connect(); + console.log('Attempting to reconnect to ProPresenter...') + propresenter_status = 'reconnecting' + SendStatusMessage() + propresenter_connect() } function presentationbridge_connect() { - let host = config.get('presentationbridgeHost'); - let port = config.get('presentationbridgePort'); - let bridgeID = config.get('presentationbridgeID'); - let password = config.get('presentationbridgePassword'); - - bridgeIO = socketio('http://' + host + ':' + port, { 'forceNew': true }); - presentationbridge_status = 'connecting'; - console.log('Connecting to Presentation Bridge Server...' + host + ' : ' + port); - SendStatusMessage(); - - bridgeIO.on('connect', function() { - console.log('Bridge Connected. Authenticating...'); - bridgeIO.emit('bridgerooms_authenticate', bridgeID, password); - presentationbridge_status = 'connected'; - SendStatusMessage(); - }); - - bridgeIO.on('bridgerooms_authenticated', function(value) { - console.log('Bridge Authenticated: ' + value); + let host = config.get('presentationbridgeHost') + let port = config.get('presentationbridgePort') + let bridgeID = config.get('presentationbridgeID') + let password = config.get('presentationbridgePassword') + + bridgeIO = socketio('http://' + host + ':' + port, { forceNew: true }) + presentationbridge_status = 'connecting' + console.log('Connecting to Presentation Bridge Server...' + host + ' : ' + port) + SendStatusMessage() + + bridgeIO.on('connect', function () { + console.log('Bridge Connected. Authenticating...') + bridgeIO.emit('bridgerooms_authenticate', bridgeID, password) + presentationbridge_status = 'connected' + SendStatusMessage() + }) + + bridgeIO.on('bridgerooms_authenticated', function (value) { + console.log('Bridge Authenticated: ' + value) if (value) { - bridgeConnected = true; - presentationbridge_status = 'authenticated'; - SendStatusMessage(); - bridgeIO.emit('gotologo', bridgeID, false); - trayMenuItems[1].label = 'Connected: ' + host + ':' + port; - buildTray(); - } - else { - bridgeConnected = false; - console.log('Invalid Bridge Password.'); - presentationbridge_status = 'badpassword'; - trayMenuItems[1].label = 'Bad Password: ' + host + ':' + port; - buildTray(); - SendStatusMessage(); + bridgeConnected = true + presentationbridge_status = 'authenticated' + SendStatusMessage() + bridgeIO.emit('gotologo', bridgeID, false) + trayMenuItems[1].label = 'Connected: ' + host + ':' + port + buildTray() + } else { + bridgeConnected = false + console.log('Invalid Bridge Password.') + presentationbridge_status = 'badpassword' + trayMenuItems[1].label = 'Bad Password: ' + host + ':' + port + buildTray() + SendStatusMessage() } - }); - - bridgeIO.on('bridgerooms_inuse', function(value) { - console.log('The selected Presentation Bridge is already in use on the server.'); - presentationbridge_status = 'inuse'; - trayMenuItems[1].label = 'Bridge Already In Use: ' + host + ':' + port; - buildTray(); - SendStatusMessage(); - }); - - bridgeIO.on('bridgeid_invalid', function() { - console.log('The provided Presentation Bridge ID is invalid.'); - presentationbridge_status = 'badid'; - trayMenuItems[1].label = 'Invalid Bridge ID: ' + host + ':' + port; - buildTray(); - SendStatusMessage(); - }); + }) + + bridgeIO.on('bridgerooms_inuse', function (value) { + console.log('The selected Presentation Bridge is already in use on the server.') + presentationbridge_status = 'inuse' + trayMenuItems[1].label = 'Bridge Already In Use: ' + host + ':' + port + buildTray() + SendStatusMessage() + }) + + bridgeIO.on('bridgeid_invalid', function () { + console.log('The provided Presentation Bridge ID is invalid.') + presentationbridge_status = 'badid' + trayMenuItems[1].label = 'Invalid Bridge ID: ' + host + ':' + port + buildTray() + SendStatusMessage() + }) bridgeIO.on('error', function (err) { try { if (err.errno.indexOf('ECONNREFUSED') > -1) { - presentationbridge_status = 'econnrefused'; - SendStatusMessage(); - trayMenuItems[1].label = 'Connection Refused: ' + ip + ':' + port; - buildTray(); - } - else if (err.errno.indexOf('ETIMEDOUT') > -1) { - presentationbridge_status = 'etimedout'; - SendStatusMessage(); - trayMenuItems[1].label = 'Connection Timed Out: ' + ip + ':' + port; - buildTray(); + presentationbridge_status = 'econnrefused' + SendStatusMessage() + trayMenuItems[1].label = 'Connection Refused: ' + ip + ':' + port + buildTray() + } else if (err.errno.indexOf('ETIMEDOUT') > -1) { + presentationbridge_status = 'etimedout' + SendStatusMessage() + trayMenuItems[1].label = 'Connection Timed Out: ' + ip + ':' + port + buildTray() } - } - catch(error) { + } catch (error) {} + }) - } - }); - - bridgeIO.on('disconnect', function() { - bridgeConnected = false; - console.log('Disconnected from Presentation Bridge Server.'); - presentationbridge_status = 'disconnected'; - trayMenuItems[1].label = 'Disconnected: ' + host + ':' + port; - buildTray(); - SendStatusMessage(); - }); + bridgeIO.on('disconnect', function () { + bridgeConnected = false + console.log('Disconnected from Presentation Bridge Server.') + presentationbridge_status = 'disconnected' + trayMenuItems[1].label = 'Disconnected: ' + host + ':' + port + buildTray() + SendStatusMessage() + }) } function presentationbridge_disconnect() { - bridgeIO.emit('bridgerooms_disconnect', config.get('presentationbridgeID')); - bridgeIO.disconnect(); - bridgeIO = null; + bridgeIO.emit('bridgerooms_disconnect', config.get('presentationbridgeID')) + bridgeIO.disconnect() + bridgeIO = null } function presentationbridge_gotologo() { - bridgeIO.emit('gotologo', config.get('presentationbridgeID'), true); - console.log('Turning on logo.'); - trayMenuItems[3].label = 'Turn Off Logo'; - trayMenuItems[3].click = function () { presentationbridge_turnofflogo(); } - buildTray(); + bridgeIO.emit('gotologo', config.get('presentationbridgeID'), true) + console.log('Turning on logo.') + trayMenuItems[3].label = 'Turn Off Logo' + trayMenuItems[3].click = function () { + presentationbridge_turnofflogo() + } + buildTray() } function presentationbridge_turnofflogo() { - bridgeIO.emit('gotologo', config.get('presentationbridgeID'), false); - console.log('Turning off logo.'); - trayMenuItems[3].label = 'Go To Logo'; - trayMenuItems[3].click = function () { presentationbridge_gotologo(); } - buildTray(); + bridgeIO.emit('gotologo', config.get('presentationbridgeID'), false) + console.log('Turning off logo.') + trayMenuItems[3].label = 'Go To Logo' + trayMenuItems[3].click = function () { + presentationbridge_gotologo() + } + buildTray() } function turnOffCommands() { - commands_on = false; - console.log('Turning off all commands.'); - trayMenuItems[4].label = 'Turn On Commands'; - trayMenuItems[4].click = function () { turnOnCommands(); } - buildTray(); + commands_on = false + console.log('Turning off all commands.') + trayMenuItems[4].label = 'Turn On Commands' + trayMenuItems[4].click = function () { + turnOnCommands() + } + buildTray() } function turnOnCommands() { - commands_on = true; - console.log('Turning on all commands.'); - trayMenuItems[4].label = 'Turn Off Commands'; - trayMenuItems[4].click = function () { turnOffCommands(); } - buildTray(); + commands_on = true + console.log('Turning on all commands.') + trayMenuItems[4].label = 'Turn Off Commands' + trayMenuItems[4].click = function () { + turnOffCommands() + } + buildTray() } function turnOffLyrics() { - lyrics_on = false; - console.log('Turning off all cloud lyrics.'); - clearLyrics(); - trayMenuItems[5].label = 'Turn On Lyrics'; - trayMenuItems[5].click = function () { turnOnLyrics(); } - buildTray(); + lyrics_on = false + console.log('Turning off all cloud lyrics.') + clearLyrics() + trayMenuItems[5].label = 'Turn On Lyrics' + trayMenuItems[5].click = function () { + turnOnLyrics() + } + buildTray() } function turnOnLyrics() { - lyrics_on = true; - console.log('Turning on all cloud lyrics.'); - trayMenuItems[5].label = 'Turn Off Lyrics'; - trayMenuItems[5].click = function () { turnOffLyrics(); } - buildTray(); + lyrics_on = true + console.log('Turning on all cloud lyrics.') + trayMenuItems[5].label = 'Turn Off Lyrics' + trayMenuItems[5].click = function () { + turnOffLyrics() + } + buildTray() } function clearLyrics() { if (bridgeConnected) { - bridgeIO.emit('current_slide', config.get('presentationbridgeID'), ''); - bridgeIO.emit('current_slide_notes', config.get('presentationbridgeID'), ''); - bridgeIO.emit('next_slide', config.get('presentationbridgeID'), ''); - bridgeIO.emit('next_slide_notes', config.get('presentationbridgeID'), ''); + bridgeIO.emit('current_slide', config.get('presentationbridgeID'), '') + bridgeIO.emit('current_slide_notes', config.get('presentationbridgeID'), '') + bridgeIO.emit('next_slide', config.get('presentationbridgeID'), '') + bridgeIO.emit('next_slide_notes', config.get('presentationbridgeID'), '') } } function handleStageDisplayMessage(message) { + var objData = JSON.parse(message) - var objData = JSON.parse(message); - - switch(objData.acn) { + switch (objData.acn) { case 'ath': if (objData.ath === true) { - console.log('ProPresenter connection authenticated.'); - propresenter_status = 'authenticated'; - SendStatusMessage(); + console.log('ProPresenter connection authenticated.') + propresenter_status = 'authenticated' + SendStatusMessage() } else { - console.log('Incorrect ProPresenter Stage Display password.'); - propresenter_status = 'badpassword'; - SendStatusMessage(); + console.log('Incorrect ProPresenter Stage Display password.') + propresenter_status = 'badpassword' + SendStatusMessage() } - break; + break case 'fv': for (let i = 0; i < objData.ary.length; i++) { if (objData.ary[i].acn === 'cs') { - if(config.get('switch_PPUCase')) { - propresenter_cs = objData.ary[i].txt.toUpperCase(); + if (config.get('switch_PPUCase')) { + propresenter_cs = objData.ary[i].txt.toUpperCase() } else { - propresenter_cs = objData.ary[i].txt; + propresenter_cs = objData.ary[i].txt } if (lyrics_on) { if (bridgeConnected) { - bridgeIO.emit('current_slide', config.get('presentationbridgeID'), propresenter_cs); - if(config.get('switch_PPimages')) { - sendProPresenterImage(objData.ary[i].uid) + bridgeIO.emit('current_slide', config.get('presentationbridgeID'), propresenter_cs) + if (config.get('switch_PPimages')) { + sendProPresenterImage(objData.ary[i].uid) } } } if (monitorWindow) { - monitorWindow.webContents.send('cs', objData.ary[i].txt); + monitorWindow.webContents.send('cs', objData.ary[i].txt) } } if (objData.ary[i].acn === 'csn') { @@ -694,41 +697,43 @@ function handleStageDisplayMessage(message) { if (commandset !== null) { for (const item of commandset) { let command = item.substring(0, item.indexOf(':')).trim().toLowerCase() - let parameters = item.substring(item.indexOf(':')+1).slice(0,-1).split(',').map(each => each.trim()) + let parameters = item + .substring(item.indexOf(':') + 1) + .slice(0, -1) + .split(',') + .map((each) => each.trim()) // remove command from notes propresenter_csn = propresenter_csn.replace(item, '') // build commands array of clean strings - propresenter_csn_commands.push({"command": command, "parameters": parameters}) + propresenter_csn_commands.push({ command: command, parameters: parameters }) } } if (propresenter_csn_commands.length > 0) { if (commands_on) { - parseStageDisplayMessage(propresenter_csn_commands); - + parseStageDisplayMessage(propresenter_csn_commands) } - } + } if (monitorWindow) { - monitorWindow.webContents.send('csn', propresenter_csn_commands); + monitorWindow.webContents.send('csn', propresenter_csn_commands) } if (lyrics_on) { if (bridgeConnected) { - bridgeIO.emit('current_slide_notes', config.get('presentationbridgeID'), propresenter_csn); + bridgeIO.emit('current_slide_notes', config.get('presentationbridgeID'), propresenter_csn) } } - } if (objData.ary[i].acn === 'ns') { - propresenter_ns = objData.ary[i].txt; + propresenter_ns = objData.ary[i].txt if (lyrics_on) { if (bridgeConnected) { - bridgeIO.emit('next_slide', config.get('presentationbridgeID'), objData.ary[i].txt); + bridgeIO.emit('next_slide', config.get('presentationbridgeID'), objData.ary[i].txt) } } if (monitorWindow) { - monitorWindow.webContents.send('ns', objData.ary[i].txt); + monitorWindow.webContents.send('ns', objData.ary[i].txt) } } if (objData.ary[i].acn === 'nsn') { @@ -740,195 +745,194 @@ function handleStageDisplayMessage(message) { if (commandset !== null) { for (const item of commandset) { let command = item.substring(0, item.indexOf(':')).trim().toLowerCase() - let parameters = item.substring(item.indexOf(':')+1).slice(0,-1).split(',').map(each => each.trim()) + let parameters = item + .substring(item.indexOf(':') + 1) + .slice(0, -1) + .split(',') + .map((each) => each.trim()) // remove command from notes propresenter_nsn = propresenter_nsn.replace(item, '') // build commands array of clean strings - propresenter_nsn_commands.push({"command": command, "parameters": parameters}) + propresenter_nsn_commands.push({ command: command, parameters: parameters }) } } if (monitorWindow) { - monitorWindow.webContents.send('nsn', propresenter_nsn_commands); + monitorWindow.webContents.send('nsn', propresenter_nsn_commands) } - + if (lyrics_on) { if (bridgeConnected) { - bridgeIO.emit('next_slide_notes', config.get('presentationbridgeID'), propresenter_nsn); + bridgeIO.emit('next_slide_notes', config.get('presentationbridgeID'), propresenter_nsn) } } } } - break; + break default: - break; + break } } function parseStageDisplayMessage(commands) { - - for (let i = 0; i < commands.length; i++) { - let command = commands[i].command - let parameters = commands[i].parameters - let commandObj = {}; - if (command !== '' && parameters) { - switch(command) { - case 'noteon': - //"noteon:0,55,100" Note On Command, Channel 1 (zero based), Note 55, Velocity 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'noteon'; - commandObj.channel = parameters[0]; - commandObj.note = parameters[1]; - commandObj.velocity = parameters[2]; - sendMidiRelayMessage(commandObj); - break; - case 'noteoff': - //"noteoff:0,55,0" Note Off Command, Channel 1 (zero based), Note 55, Velocity 0 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'noteoff'; - commandObj.channel = parameters[0]; - commandObj.note = parameters[1]; - commandObj.velocity = parameters[2]; - sendMidiRelayMessage(commandObj); - break; - case 'aftertouch': - //"aftertouch:0,55,100" Polyphonic Aftertouch Command, Channel 1 (zero based), Note 55, Value 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'aftertouch'; - commandObj.channel = parameters[0]; - commandObj.note = parameters[1]; - commandObj.value = parameters[2]; - sendMidiRelayMessage(commandObj); - break; - case 'cc': - //"cc:0,32,100" CC (Controller Change) Command, Channel 1 (zero based), Controller 32, Value 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'cc'; - commandObj.channel = parameters[0]; - commandObj.note = parameters[1]; - commandObj.value = parameters[2]; - sendMidiRelayMessage(commandObj); - break; - case 'pc': - //"pc:0,100" PC (Program Change) Command, Channel 1 (zero based), Value 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'pc'; - commandObj.channel = parameters[0]; - commandObj.value = parameters[1]; - sendMidiRelayMessage(commandObj); - break; - case 'pressure': - //"pressure:0,100" Channel Pressure / Aftertouch Command, Channel 1 (zero based), Value 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'pressure'; - commandObj.channel = parameters[0]; - commandObj.value = parameters[1]; - sendMidiRelayMessage(commandObj); - break; - case 'pitchbend': - //"pitchbend:0,100" Pitchbend Command, Channel 1 (zero based), Value 100 - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'pitchbend'; - commandObj.channel = parameters[0]; - commandObj.value = parameters[1]; - sendMidiRelayMessage(commandObj); - break; - case 'msc': - //"msc:0,lighting.general,go,1,12,," MSC Command, Device ID 0, Command Format lighting.general, Cue 1, Cuelist 12, Cue Path blank - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'msc'; - commandObj.deviceid = parameters[0]; - commandObj.commandformat = parameters[1]; - commandObj.command = parameters[2]; - commandObj.cue = parameters[3]; - commandObj.cuelist = parameters[4]; - commandObj.cuepath = parameters[5]; - sendMidiRelayMessage(commandObj); - break; - case 'sysex': - //"sysex;0xF0,0x41,0x10,0x00,0x00,0x00,0x20,0x12,0x71,0x00,0x08,0x00,0x07,0xF7" SysEx Command, Hexadecimal or Decimal format - commandObj.midiport = config.get('midirelayMIDIPort'); - commandObj.midicommand = 'sysex'; - commandObj.message = parameters[0]; - sendMidiRelayMessage(commandObj); - break; - case 'vista': - //"vista:12,1" Vista MSC Command, Cuelist 12, Cue 1 - commandObj.midiport = config.get('vistaMIDIPort'); - commandObj.midicommand = 'msc'; - commandObj.deviceid = '0'; - commandObj.commandformat = 'lighting.general'; - commandObj.command = 'go'; - commandObj.cue = parameters[1]; - commandObj.cuelist = parameters[0]; - commandObj.cuepath = ''; - sendVistaMessage(commandObj); - break; - case 'http': - //"http:post,url,{json}" - //"http:get,url" - //"http:url" - if (parameters[0] === 'POST') { - commandObj.type = 'POST'; - commandObj.url = parameters[1]; - if (parameters[2]) { - commandObj.data = parameters[2]; - } - } - else if (parameters[0] === 'GET') { - commandObj.type = 'GET'; - commandObj.url = parameters[1]; - } - else { - commandObj.type = 'GET'; - commandObj.url = parameters[0]; - } - sendHttpMessage(commandObj); - break; - case 'companion': - case 'cbp': - //"companion:1,2" - commandObj.bank = parameters[0]; - commandObj.button = parameters[1]; - sendCompanionMessage(commandObj); - break; - case 'logo': - //"logo:on" - if (parameters[0].toLowerCase() === 'on') { - presentationbridge_gotologo(); - } - else { - presentationbridge_turnofflogo(); + for (let i = 0; i < commands.length; i++) { + let command = commands[i].command + let parameters = commands[i].parameters + let commandObj = {} + if (command !== '' && parameters) { + switch (command) { + case 'noteon': + //"noteon:0,55,100" Note On Command, Channel 1 (zero based), Note 55, Velocity 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'noteon' + commandObj.channel = parameters[0] + commandObj.note = parameters[1] + commandObj.velocity = parameters[2] + sendMidiRelayMessage(commandObj) + break + case 'noteoff': + //"noteoff:0,55,0" Note Off Command, Channel 1 (zero based), Note 55, Velocity 0 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'noteoff' + commandObj.channel = parameters[0] + commandObj.note = parameters[1] + commandObj.velocity = parameters[2] + sendMidiRelayMessage(commandObj) + break + case 'aftertouch': + //"aftertouch:0,55,100" Polyphonic Aftertouch Command, Channel 1 (zero based), Note 55, Value 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'aftertouch' + commandObj.channel = parameters[0] + commandObj.note = parameters[1] + commandObj.value = parameters[2] + sendMidiRelayMessage(commandObj) + break + case 'cc': + //"cc:0,32,100" CC (Controller Change) Command, Channel 1 (zero based), Controller 32, Value 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'cc' + commandObj.channel = parameters[0] + commandObj.note = parameters[1] + commandObj.value = parameters[2] + sendMidiRelayMessage(commandObj) + break + case 'pc': + //"pc:0,100" PC (Program Change) Command, Channel 1 (zero based), Value 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'pc' + commandObj.channel = parameters[0] + commandObj.value = parameters[1] + sendMidiRelayMessage(commandObj) + break + case 'pressure': + //"pressure:0,100" Channel Pressure / Aftertouch Command, Channel 1 (zero based), Value 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'pressure' + commandObj.channel = parameters[0] + commandObj.value = parameters[1] + sendMidiRelayMessage(commandObj) + break + case 'pitchbend': + //"pitchbend:0,100" Pitchbend Command, Channel 1 (zero based), Value 100 + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'pitchbend' + commandObj.channel = parameters[0] + commandObj.value = parameters[1] + sendMidiRelayMessage(commandObj) + break + case 'msc': + //"msc:0,lighting.general,go,1,12,," MSC Command, Device ID 0, Command Format lighting.general, Cue 1, Cuelist 12, Cue Path blank + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'msc' + commandObj.deviceid = parameters[0] + commandObj.commandformat = parameters[1] + commandObj.command = parameters[2] + commandObj.cue = parameters[3] + commandObj.cuelist = parameters[4] + commandObj.cuepath = parameters[5] + sendMidiRelayMessage(commandObj) + break + case 'sysex': + //"sysex;0xF0,0x41,0x10,0x00,0x00,0x00,0x20,0x12,0x71,0x00,0x08,0x00,0x07,0xF7" SysEx Command, Hexadecimal or Decimal format + commandObj.midiport = config.get('midirelayMIDIPort') + commandObj.midicommand = 'sysex' + commandObj.message = parameters[0] + sendMidiRelayMessage(commandObj) + break + case 'vista': + //"vista:12,1" Vista MSC Command, Cuelist 12, Cue 1 + commandObj.midiport = config.get('vistaMIDIPort') + commandObj.midicommand = 'msc' + commandObj.deviceid = '0' + commandObj.commandformat = 'lighting.general' + commandObj.command = 'go' + commandObj.cue = parameters[1] + commandObj.cuelist = parameters[0] + commandObj.cuepath = '' + sendVistaMessage(commandObj) + break + case 'http': + //"http:post,url,{json}" + //"http:get,url" + //"http:url" + if (parameters[0] === 'POST') { + commandObj.type = 'POST' + commandObj.url = parameters[1] + if (parameters[2]) { + commandObj.data = parameters[2] } - break; - default: - console.log('Invalid command: ' + command); - break; - } + } else if (parameters[0] === 'GET') { + commandObj.type = 'GET' + commandObj.url = parameters[1] + } else { + commandObj.type = 'GET' + commandObj.url = parameters[0] + } + sendHttpMessage(commandObj) + break + case 'companion': + case 'cbp': + //"companion:1,2" + commandObj.bank = parameters[0] + commandObj.button = parameters[1] + sendCompanionMessage(commandObj) + break + case 'logo': + //"logo:on" + if (parameters[0].toLowerCase() === 'on') { + presentationbridge_gotologo() + } else { + presentationbridge_turnofflogo() + } + break + default: + console.log('Invalid command: ' + command) + break } } - + } } function sendMidiRelayMessage(midiObj) { if (commands_on) { if (config.get('plugin_midirelay')) { - let ip = config.get('midirelayIP'); - let port = config.get('midirelayPort'); - + let ip = config.get('midirelayIP') + let port = config.get('midirelayPort') + let options = { method: 'POST', url: 'http://' + ip + ':' + port + '/sendmidi', - data: midiObj - }; - + data: midiObj, + } + axios(options) - .then(function (response) { - console.log('midi-relay message sent.'); - }) - .catch(function (error) { - console.log('Unable to send midi-relay message.', error.errno); - }); + .then(function (response) { + console.log('midi-relay message sent.') + }) + .catch(function (error) { + console.log('Unable to send midi-relay message.', error.errno) + }) } } } @@ -936,22 +940,22 @@ function sendMidiRelayMessage(midiObj) { function sendVistaMessage(obj) { if (commands_on) { if (config.get('plugin_vista')) { - let ip = config.get('vistaIP'); - let port = config.get('vistaPort'); - + let ip = config.get('vistaIP') + let port = config.get('vistaPort') + let options = { method: 'POST', url: 'http://' + ip + ':' + port + '/sendmidi', - data: obj - }; - + data: obj, + } + axios(options) - .then(function (response) { - console.log('Vista midi-relay message sent.'); - }) - .catch(function (error) { - console.log('Unable to send Vista midi-relay message.', error.errno); - }); + .then(function (response) { + console.log('Vista midi-relay message sent.') + }) + .catch(function (error) { + console.log('Unable to send Vista midi-relay message.', error.errno) + }) } } } @@ -961,55 +965,59 @@ function sendHttpMessage(httpObj) { if (config.get('plugin_http')) { let options = { method: httpObj.method, - url: httpObj.url - }; - + url: httpObj.url, + } + if (httpObj.data) { - options.data = httpObj.data; + options.data = httpObj.data } - + axios(options) - .then(function (response) { - console.log('HTTP message sent.'); - }) - .catch(function (error) { - console.log('Unable to send HTTP message.', error.errno); - }); + .then(function (response) { + console.log('HTTP message sent.') + }) + .catch(function (error) { + console.log('Unable to send HTTP message.', error.errno) + }) } } } function sendProPresenterImage(slideUID) { - const ip = config.get('propresenterIP'); - const port = config.get('propresenterPort'); - - const url = ('http://' + ip + ':' + port + '/stage/image/' + slideUID); + const ip = config.get('propresenterIP') + const port = config.get('propresenterPort') + + const url = 'http://' + ip + ':' + port + '/stage/image/' + slideUID let options = { method: 'GET', url: url, - responseType: 'arraybuffer' - }; + responseType: 'arraybuffer', + } axios(options) - .then(function (response) { - if (response.data !== undefined && response.data !== null) { - bridgeIO.emit('current_slide_image', config.get('presentationbridgeID'), Buffer.from(response.data, 'binary').toString('base64')) - } - }) - .catch(function (error) { - console.log('ProPresenter Image GET error', error.errno); - }); + .then(function (response) { + if (response.data !== undefined && response.data !== null) { + bridgeIO.emit( + 'current_slide_image', + config.get('presentationbridgeID'), + Buffer.from(response.data, 'binary').toString('base64') + ) + } + }) + .catch(function (error) { + console.log('ProPresenter Image GET error', error.errno) + }) } -function createCompanionConnection() { +function createCompanionConnection() { if (config.get('plugin_companion')) { - if (companionClient !== undefined){ + if (companionClient !== undefined) { companionClient.destroy() companionClient = undefined } - companionClient = new net.createConnection(config.get('companionPort'), config.get('companionIP')); - console.log('Connecting to Companion.'); + companionClient = new net.createConnection(config.get('companionPort'), config.get('companionIP')) + console.log('Connecting to Companion.') companionClient.on('connect', () => { console.log('Companion Connected') @@ -1037,76 +1045,76 @@ function createCompanionConnection() { companionClient = undefined } } -function sendCompanionMessage(companionObj) { +function sendCompanionMessage(companionObj) { if (commands_on) { if (config.get('plugin_companion')) { if (companionClient === undefined) { - createCompanionConnection(); + createCompanionConnection() + } + let bank = companionObj.bank + let button = companionObj.button + if (companionClient !== undefined) { + console.log('Companion Message: ' + bank + ', ' + button) + companionClient.write(`BANK-PRESS ${bank} ${button}\r\n`) } - let bank = companionObj.bank; - let button = companionObj.button; - if (companionClient !== undefined) { - console.log('Companion Message: '+ bank + ', ' + button) - companionClient.write(`BANK-PRESS ${bank} ${button}\r\n`); - } - } + } } } function SendStatusMessage() { if (settingsWindow) { - settingsWindow.webContents.send('propresenter_status', propresenter_status); - settingsWindow.webContents.send('presentationbridge_status', presentationbridge_status); + settingsWindow.webContents.send('propresenter_status', propresenter_status) + settingsWindow.webContents.send('presentationbridge_status', presentationbridge_status) } } //IPCs ipcMain.on('propresenter_status', function (event) { - event.sender.send('propresenter_status', propresenter_status); -}); + event.sender.send('propresenter_status', propresenter_status) +}) ipcMain.on('monitor_status', function (event) { - event.sender.send('cs', propresenter_cs); - event.sender.send('csn', propresenter_csn_commands); - event.sender.send('ns', propresenter_ns); - event.sender.send('nsn', propresenter_nsn_commands); -}); + event.sender.send('cs', propresenter_cs) + event.sender.send('csn', propresenter_csn_commands) + event.sender.send('ns', propresenter_ns) + event.sender.send('nsn', propresenter_nsn_commands) +}) ipcMain.on('mdns_propresenter_hosts', function (event) { - event.sender.send('mdns_propresenter_hosts', mdns_propresenter_hosts); -}); + event.sender.send('mdns_propresenter_hosts', mdns_propresenter_hosts) +}) ipcMain.on('mdns_midirelay_hosts', function (event) { - event.sender.send('mdns_midirelay_hosts', mdns_midirelay_hosts); -}); + event.sender.send('mdns_midirelay_hosts', mdns_midirelay_hosts) +}) ipcMain.on('mdns_rescan', function (event) { - findHosts(); -}); + findHosts() +}) ipcMain.on('propresenter_connect', function (event, ip, port, password) { - propresenter_disconnect(false); - propresenter_status = 'connecting'; - SendStatusMessage(); - setTimeout(propresenter_connect, 2000); -}); + propresenter_disconnect(false) + propresenter_status = 'connecting' + SendStatusMessage() + setTimeout(propresenter_connect, 2000) +}) ipcMain.on('propresenter_disconnect', function (event) { - propresenter_disconnect(false); -}); + propresenter_disconnect(false) +}) ipcMain.on('propresenter_force_disconnect', function (event) { - propresenter_disconnect(false); -}); + propresenter_disconnect(false) +}) ipcMain.on('presentationbridge_connect', function (event, host, port, id, password) { - presentationbridge_connect(); -}); + presentationbridge_connect() +}) ipcMain.on('presentationbridge_disconnect', function (event) { - presentationbridge_disconnect(); -}); + presentationbridge_disconnect() +}) ipcMain.on('presentationbridge_force_disconnect', function (event) { - presentationbridge_disconnect(); -}); + presentationbridge_disconnect() +}) diff --git a/menu.js b/menu.js index a188429..70fc11d 100644 --- a/menu.js +++ b/menu.js @@ -1,28 +1,21 @@ -'use strict'; -const path = require('path'); -const {app, Menu, shell} = require('electron'); -const { - is, - appMenu, - aboutMenuItem, - openUrlMenuItem, - openNewGitHubIssue, - debugInfo -} = require('electron-util'); -const config = require('./config'); +'use strict' +const path = require('path') +const { app, Menu, shell } = require('electron') +const { is, appMenu, aboutMenuItem, openUrlMenuItem, openNewGitHubIssue, debugInfo } = require('electron-util') +const config = require('./config') const showPreferences = () => { // Show the app's preferences here -}; +} const helpSubmenu = [ openUrlMenuItem({ label: 'Website', - url: 'https://techministry.blog' + url: 'https://techministry.blog', }), openUrlMenuItem({ label: 'Source Code', - url: 'https://github.com/josephdadams/presentation-bridge-link' + url: 'https://github.com/josephdadams/presentation-bridge-link', }), { label: 'Report an Issue…', @@ -33,62 +26,62 @@ const helpSubmenu = [ --- -${debugInfo()}`; +${debugInfo()}` openNewGitHubIssue({ user: 'josephdadams', repo: 'presentation-bridge-client', - body - }); - } - } -]; + body, + }) + }, + }, +] if (!is.macos) { helpSubmenu.push( { - type: 'separator' + type: 'separator', }, aboutMenuItem({ icon: path.join(__dirname, 'static', 'icon.png'), - text: 'Created by Joseph Adams (techministry.blog)' + text: 'Created by Joseph Adams (techministry.blog)', }) - ); + ) } const debugSubmenu = [ { label: 'Show Settings', click() { - config.openInEditor(); - } + config.openInEditor() + }, }, { label: 'Show App Data', click() { - shell.openItem(app.getPath('userData')); - } + shell.openItem(app.getPath('userData')) + }, }, { - type: 'separator' + type: 'separator', }, { label: 'Delete Settings', click() { - config.clear(); - app.relaunch(); - app.quit(); - } + config.clear() + app.relaunch() + app.quit() + }, }, { label: 'Delete App Data', click() { - shell.moveItemToTrash(app.getPath('userData')); - app.relaunch(); - app.quit(); - } - } -]; + shell.moveItemToTrash(app.getPath('userData')) + app.relaunch() + app.quit() + }, + }, +] const macosTemplate = [ appMenu([ @@ -96,38 +89,38 @@ const macosTemplate = [ label: 'Preferences…', accelerator: 'Command+,', click() { - showPreferences(); - } - } + showPreferences() + }, + }, ]), { role: 'fileMenu', submenu: [ { - label: 'Custom' + label: 'Custom', }, { - type: 'separator' + type: 'separator', }, { - role: 'close' - } - ] + role: 'close', + }, + ], }, { - role: 'editMenu' + role: 'editMenu', }, { - role: 'viewMenu' + role: 'viewMenu', }, { - role: 'windowMenu' + role: 'windowMenu', }, { role: 'help', - submenu: helpSubmenu - } -]; + submenu: helpSubmenu, + }, +] // Linux and Windows const otherTemplate = [ @@ -135,45 +128,45 @@ const otherTemplate = [ role: 'fileMenu', submenu: [ { - label: 'Custom' + label: 'Custom', }, { - type: 'separator' + type: 'separator', }, { label: 'Settings', accelerator: 'Control+,', click() { - showPreferences(); - } + showPreferences() + }, }, { - type: 'separator' + type: 'separator', }, { - role: 'quit' - } - ] + role: 'quit', + }, + ], }, { - role: 'editMenu' + role: 'editMenu', }, { - role: 'viewMenu' + role: 'viewMenu', }, { role: 'help', - submenu: helpSubmenu - } -]; + submenu: helpSubmenu, + }, +] -const template = process.platform === 'darwin' ? macosTemplate : otherTemplate; +const template = process.platform === 'darwin' ? macosTemplate : otherTemplate if (is.development) { template.push({ label: 'Debug', - submenu: debugSubmenu - }); + submenu: debugSubmenu, + }) } -module.exports = Menu.buildFromTemplate(template); +module.exports = Menu.buildFromTemplate(template) diff --git a/monitor.html b/monitor.html index 1eb6116..1e77384 100644 --- a/monitor.html +++ b/monitor.html @@ -1,50 +1,51 @@ - + - + PresentationBridge Client Monitor - - - - - + + + + + @@ -277,13 +293,13 @@

PresentationBridge Client Monitor

Created by techministry.blog

-
Current Slide:
-
-
-
 
-
Next Slide:
-
-
+
Current Slide:
+
+
+
 
+
Next Slide:
+
+
diff --git a/package.json b/package.json index f630af6..9f9acf2 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "generateUpdatesFilesForAllChannels": false }, "dependencies": { -"@popperjs/core": "^2.9.2", + "@popperjs/core": "^2.9.2", "axios": "^1.6.0", "bootstrap": "^4.6.0", "electron-context-menu": "^0.15.0", diff --git a/readme.md b/readme.md index 601fe52..9e4740e 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ Links data from your Presentation/lyrics software to other software. See [help.m ## Install -*macOS 10.10+, Linux, and Windows 7+ are supported (64-bit only).* +_macOS 10.10+, Linux, and Windows 7+ are supported (64-bit only)._ **macOS** @@ -14,16 +14,14 @@ Links data from your Presentation/lyrics software to other software. See [help.m [**Download**](https://github.com/josephdadams/presentationbridge-client/releases/latest) the `.AppImage` or `.deb` file. -*The AppImage needs to be [made executable](http://discourse.appimage.org/t/how-to-make-an-appimage-executable/80) after download.* +_The AppImage needs to be [made executable](http://discourse.appimage.org/t/how-to-make-an-appimage-executable/80) after download._ **Windows** [**Download**](https://github.com/josephdadams/presentationbridge-client/releases/latest) the `.exe` file. - --- - ## Dev Built with [Electron](https://electronjs.org). @@ -41,4 +39,4 @@ $ yarn start $ yarn run release ``` -After Travis finishes building your app, open the release draft it created and click "Publish". \ No newline at end of file +After Travis finishes building your app, open the release draft it created and click "Publish". diff --git a/static/mask.ip-input.js b/static/mask.ip-input.js index f8a97bc..123a20b 100644 --- a/static/mask.ip-input.js +++ b/static/mask.ip-input.js @@ -1,203 +1,188 @@ -(function($) { - String.prototype.isIpv4 = function() { - rgx = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/; - return rgx.test(this.toString()); - }; - String.prototype.isIpv6 = function() { - rgx = /\b([A-F0-9]{1,4}:){7}([A-F0-9]{1,4})\b/i; - return rgx.test(this.toString()); - }; - - String.prototype.insert = function(str, pos) { - if (typeof pos == "undefined") pos = 0; - if (typeof str == "undefined") str = ""; - - return this.slice(0, pos) + str + this.slice(pos); - }; - - String.prototype.getSymbolCount = function(char = ".") { - if (this.indexOf(char) == -1) return 0; - - let charArray = this.split("").sort(); - let count = 0; - - for ( - let index = charArray.indexOf(char); - index < charArray.length; - index++ - ) { - if (charArray[index] != char) break; - count++; - } - - return count; - }; - - $.fn.getCursorPosition = function() { - var input = this.get(0); - if (!input) return; - if ("selectionStart" in input) { - return input.selectionStart; - } else if (document.selection) { - // IE - input.focus(); - var selection = document.selection.createRange(); - var selectionLength = document.selection.createRange().text.length; - sel.moveStart("character", -input.value.length); - return selection.text.length - selectionLength; - } - }; - - $.fn.setCursorPosition = function(position) { - if (this != null) { - if ($(this)[0].createTextRange) { - var range = $(this)[0].createTextRange(); - range.move("character", position); - range.select(); - } else { - if ($(this)[0].selectionStart) { - $(this)[0].focus(); - $(this)[0].setSelectionRange(position, position); - } else { - $(this)[0].focus(); - } - } - } - }; - - $.fn.ipAddress = function() { - let rgx = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\b/; - this.each(function(index, item) { - if ($(item).attr("placeholder") == undefined) - $(item).attr("placeholder", "000.000.000.000"); - }); - - $(this).on("blur", function(e) { - let that = this; - if (that.value.length > 0 && !that.value.isIpv4()) { - $(this).addClass("is-invalid"); - e.preventDefault(); - return false; - } - }); - - $(this).on("keyup", function(e) { - let that = this; - if ( - that.value.split(".").filter(function(current, index, arr) { - return current.length > 0 && !rgx.test(current); - }).length == 0 - ) { - if (that.value.length > 0 && !that.value.isIpv4()) - $(this).addClass("is-invalid"); - - $(this).removeClass("is-invalid"); - return true; - } else { - $(this).addClass("is-invalid"); - return true; - } - }); - - $(this).on("keydown", function(e) { - let that = this; - let pointIndex; - let position = $(that).getCursorPosition(); - switch (e.which) { - case 8: /*backspace*/ - case 46: /*del*/ - case 9: /*tab*/ - case 37: /*left arrow*/ - case 38: /*up arrow*/ - case 39: /*right arrow*/ - case 40 /*down arrow*/: - pointIndex = that.value.lastIndexOf(".", position); - pointIndex = pointIndex < 0 ? 0 : pointIndex + 1; - if ( - that.value.split(".").filter(function(current, index, arr) { - return current.length > 0 && !rgx.test(current); - }).length == 0 - ) { - $(this).removeClass("is-invalid"); - return true; - } else { - $(this).addClass("is-invalid"); - return true; - } - return false; - default: - if (e.ctrlKey) { - switch (e.which) { - case 65: /*a*/ //ctrl+a - case 67: /*c*/ //ctrl+c - case 86 /*v*/: //ctrl+v - return true; - case 88 /*x*/: //ctrl+x - $(this).removeClass("is-invalid"); - return true; - - default: - break; - } - } - break; - } - - that = $.extend(that, { newValue: that.value }); - - if (that.value.length != position) { - e.preventDefault(); - that.newValue = that.value.insert(e.key, position); - if (!that.newValue.isIpv4()) return false; - that.value = that.newValue; - $(that).setCursorPosition(position + 1); - return true; - } - - pointIndex = that.value.lastIndexOf(".", position); - pointIndex = pointIndex < 0 ? 0 : pointIndex + 1; - - if ( - that.value.length >= 15 || - (that.newValue.getSymbolCount(".") >= 3 && - that.value.length - pointIndex >= 3) - ) - return false; - - if ( - !/[0-9\.]/.test(e.key) || - ((that.newValue[that.newValue.length - 1] == "." || - that.value == "" || - that.value.getSymbolCount(".") >= 3) && - e.key == ".") - ) { - $(this).addClass("is-invalid"); - return false; - } - - if (pointIndex >= 0 && that.value.length - pointIndex >= 3) { - if (that.value.getSymbolCount(".") >= 3) { - $(this).addClass("is-invalid"); - return false; - } - if (e.key != ".") that.value += "."; - } - if ( - ( - that.value + - (e.key == "." || that.value.length - pointIndex == 3 ? "." : e.key) - ) - .split(".") - .filter(function(current, index, arr) { - return current.length > 0 && !rgx.test(current); - }).length > 0 - ) { - $(this).addClass("is-invalid"); - return false; - } - $(this).removeClass("is-invalid"); - that.newValue = that.value + e.key; - return true; - }); - }; -})(jQuery); +;(function ($) { + String.prototype.isIpv4 = function () { + rgx = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/ + return rgx.test(this.toString()) + } + String.prototype.isIpv6 = function () { + rgx = /\b([A-F0-9]{1,4}:){7}([A-F0-9]{1,4})\b/i + return rgx.test(this.toString()) + } + + String.prototype.insert = function (str, pos) { + if (typeof pos == 'undefined') pos = 0 + if (typeof str == 'undefined') str = '' + + return this.slice(0, pos) + str + this.slice(pos) + } + + String.prototype.getSymbolCount = function (char = '.') { + if (this.indexOf(char) == -1) return 0 + + let charArray = this.split('').sort() + let count = 0 + + for (let index = charArray.indexOf(char); index < charArray.length; index++) { + if (charArray[index] != char) break + count++ + } + + return count + } + + $.fn.getCursorPosition = function () { + var input = this.get(0) + if (!input) return + if ('selectionStart' in input) { + return input.selectionStart + } else if (document.selection) { + // IE + input.focus() + var selection = document.selection.createRange() + var selectionLength = document.selection.createRange().text.length + sel.moveStart('character', -input.value.length) + return selection.text.length - selectionLength + } + } + + $.fn.setCursorPosition = function (position) { + if (this != null) { + if ($(this)[0].createTextRange) { + var range = $(this)[0].createTextRange() + range.move('character', position) + range.select() + } else { + if ($(this)[0].selectionStart) { + $(this)[0].focus() + $(this)[0].setSelectionRange(position, position) + } else { + $(this)[0].focus() + } + } + } + } + + $.fn.ipAddress = function () { + let rgx = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\b/ + this.each(function (index, item) { + if ($(item).attr('placeholder') == undefined) $(item).attr('placeholder', '000.000.000.000') + }) + + $(this).on('blur', function (e) { + let that = this + if (that.value.length > 0 && !that.value.isIpv4()) { + $(this).addClass('is-invalid') + e.preventDefault() + return false + } + }) + + $(this).on('keyup', function (e) { + let that = this + if ( + that.value.split('.').filter(function (current, index, arr) { + return current.length > 0 && !rgx.test(current) + }).length == 0 + ) { + if (that.value.length > 0 && !that.value.isIpv4()) $(this).addClass('is-invalid') + + $(this).removeClass('is-invalid') + return true + } else { + $(this).addClass('is-invalid') + return true + } + }) + + $(this).on('keydown', function (e) { + let that = this + let pointIndex + let position = $(that).getCursorPosition() + switch (e.which) { + case 8: /*backspace*/ + case 46: /*del*/ + case 9: /*tab*/ + case 37: /*left arrow*/ + case 38: /*up arrow*/ + case 39: /*right arrow*/ + case 40 /*down arrow*/: + pointIndex = that.value.lastIndexOf('.', position) + pointIndex = pointIndex < 0 ? 0 : pointIndex + 1 + if ( + that.value.split('.').filter(function (current, index, arr) { + return current.length > 0 && !rgx.test(current) + }).length == 0 + ) { + $(this).removeClass('is-invalid') + return true + } else { + $(this).addClass('is-invalid') + return true + } + return false + default: + if (e.ctrlKey) { + switch (e.which) { + case 65: /*a*/ //ctrl+a + case 67: /*c*/ //ctrl+c + case 86 /*v*/: //ctrl+v + return true + case 88 /*x*/: //ctrl+x + $(this).removeClass('is-invalid') + return true + + default: + break + } + } + break + } + + that = $.extend(that, { newValue: that.value }) + + if (that.value.length != position) { + e.preventDefault() + that.newValue = that.value.insert(e.key, position) + if (!that.newValue.isIpv4()) return false + that.value = that.newValue + $(that).setCursorPosition(position + 1) + return true + } + + pointIndex = that.value.lastIndexOf('.', position) + pointIndex = pointIndex < 0 ? 0 : pointIndex + 1 + + if (that.value.length >= 15 || (that.newValue.getSymbolCount('.') >= 3 && that.value.length - pointIndex >= 3)) + return false + + if ( + !/[0-9\.]/.test(e.key) || + ((that.newValue[that.newValue.length - 1] == '.' || that.value == '' || that.value.getSymbolCount('.') >= 3) && + e.key == '.') + ) { + $(this).addClass('is-invalid') + return false + } + + if (pointIndex >= 0 && that.value.length - pointIndex >= 3) { + if (that.value.getSymbolCount('.') >= 3) { + $(this).addClass('is-invalid') + return false + } + if (e.key != '.') that.value += '.' + } + if ( + (that.value + (e.key == '.' || that.value.length - pointIndex == 3 ? '.' : e.key)) + .split('.') + .filter(function (current, index, arr) { + return current.length > 0 && !rgx.test(current) + }).length > 0 + ) { + $(this).addClass('is-invalid') + return false + } + $(this).removeClass('is-invalid') + that.newValue = that.value + e.key + return true + }) + } +})(jQuery)