mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-05 13:47:49 +02:00
Import WebThrottle-EX from ca33f6a
This commit is contained in:
156
ram/driver/static/wt/js/addloco.js
Normal file
156
ram/driver/static/wt/js/addloco.js
Normal file
@@ -0,0 +1,156 @@
|
||||
$(document).ready(function(){
|
||||
locoList = getLocoList();
|
||||
savedMaps = getPreparedMaps();
|
||||
$("#loco-form").on("submit", function (e) {
|
||||
e.preventDefault();
|
||||
data = $(this).serializeArray();
|
||||
mode = $("#loco-submit").attr("loco-mode");
|
||||
if(mode != 'edit'){
|
||||
saveLocomotive(data);
|
||||
}else{
|
||||
id = $("#loco-submit").attr("loco-id")
|
||||
saveEditedLocomotive(data, id);
|
||||
}
|
||||
locoList = getLocoList();
|
||||
loadLocomotives();
|
||||
$("#loco-form")[0].reset();
|
||||
$("#loco-form-content").css("display", "none");
|
||||
});
|
||||
|
||||
$("#add-loco").on("click", function () {
|
||||
savedMaps = getPreparedMaps();
|
||||
$("#loco-form")[0].reset();
|
||||
$("#loco-form-content").css("display", "inline-block");
|
||||
$(".add-loco-form .add-loco-head").html("Add Locomotive");
|
||||
$("#loco-submit").attr("loco-mode", "add");
|
||||
});
|
||||
|
||||
$("#close-addloco-model").on("click", function () {
|
||||
if ($("#loco-form-content").is(":visible")) {
|
||||
$("#loco-form-content").css("display", "none");
|
||||
}
|
||||
});
|
||||
|
||||
$("#ex-locoid").autocomplete({
|
||||
delay: 0,
|
||||
minLength: 0,
|
||||
source: function (request, response) {
|
||||
var matcher = new RegExp(
|
||||
$.ui.autocomplete.escapeRegex(request.term),
|
||||
"i"
|
||||
);
|
||||
response(
|
||||
$.grep(locoList, function (item) {
|
||||
if (item != undefined) {
|
||||
console.log(item);
|
||||
return (
|
||||
matcher.test(item.name) ||
|
||||
matcher.test(item.cv) ||
|
||||
matcher.test(item.type) ||
|
||||
matcher.test(item.manufacturer)
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
select: function (event, ui) {
|
||||
$(this).val(ui.item.cv + " | " + ui.item.name);
|
||||
$(this).attr("loco-cv", ui.item.cv);
|
||||
return false;
|
||||
},
|
||||
})
|
||||
.focus(function () {
|
||||
console.log("Focused");
|
||||
$(this).autocomplete("search", "");
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function (ul, item) {
|
||||
$(ul).addClass("res-list");
|
||||
return $("<li></li>")
|
||||
.data("item.autocomplete", item)
|
||||
.append(
|
||||
"<div><p class='ac-loco-name'>" +
|
||||
item.name +
|
||||
"</p><small> <span class='pill'>CV:" +
|
||||
item.cv +
|
||||
"</span>|<span class='pill'>" +
|
||||
item.type +
|
||||
"</span>|<span class='pill wrap'>" +
|
||||
item.brand +
|
||||
"</span></small></div>"
|
||||
)
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
$("#function-maps").autocomplete({
|
||||
delay: 0,
|
||||
minLength: 0,
|
||||
source: function (request, response) {
|
||||
var matcher = new RegExp(
|
||||
$.ui.autocomplete.escapeRegex(request.term),
|
||||
"i"
|
||||
);
|
||||
response(
|
||||
$.grep(savedMaps, function (item) {
|
||||
return matcher.test(item.mname);
|
||||
})
|
||||
);
|
||||
},
|
||||
select: function (event, ui) {
|
||||
$(this).val(ui.item.mname);
|
||||
return false;
|
||||
},
|
||||
})
|
||||
.focus(function () {
|
||||
console.log("Focused");
|
||||
$(this).autocomplete("search", "");
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function (ul, item) {
|
||||
$(ul).addClass("res-list-sm");
|
||||
return $("<li></li>")
|
||||
.data("item.autocomplete", item)
|
||||
.append("<div><p class='item'>" + item.mname + "</p></div>")
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
$("#export-locolist").on("click", function (e) {
|
||||
downloadCabData();
|
||||
});
|
||||
|
||||
$("#import-locolist").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
$("#cabs-upload").trigger("click");
|
||||
locoList = getLocoList();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function getPreparedMaps() {
|
||||
const defaultMap = {
|
||||
mname: "Default",
|
||||
fnData: {},
|
||||
}
|
||||
return [defaultMap, ...getMapData()];
|
||||
}
|
||||
|
||||
/** Reference Map Structure
|
||||
maps = [
|
||||
{
|
||||
mname: "mkmap",
|
||||
fnData: {
|
||||
f0: [0, 1, "Head Light", 1],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
locos = [
|
||||
{
|
||||
name: "Mikado",
|
||||
cv: 3,
|
||||
type: "Diesel",
|
||||
manufacturer: "Kato",
|
||||
},
|
||||
];
|
||||
|
||||
*/
|
270
ram/driver/static/wt/js/commandController.js
Normal file
270
ram/driver/static/wt/js/commandController.js
Normal file
@@ -0,0 +1,270 @@
|
||||
/* This is part of the DCC++ EX Project for model railroading and more.
|
||||
For licence information, please see index.html
|
||||
For more information, see us at dcc-ex.com.
|
||||
|
||||
commandController.js
|
||||
|
||||
Open a serial port and create a stream to read and write data
|
||||
While there is data, we read the results in loop function
|
||||
*/
|
||||
$(document).ready(function(){
|
||||
console.log("Command Controller loaded");
|
||||
uiDisable(true)
|
||||
emulatorClass = new Emulator({logger: displayLog});
|
||||
});
|
||||
|
||||
// - Request a port and open an asynchronous connection,
|
||||
// which prevents the UI from blocking when waiting for
|
||||
// input, and allows serial to be received by the web page
|
||||
// whenever it arrives.
|
||||
async function connectServer() {
|
||||
// Gets values of the connection method selector
|
||||
selectMethod = document.getElementById('select-method')
|
||||
mode = selectMethod.value;
|
||||
// Disables selector so it can't be changed whilst connected
|
||||
selectMethod.disabled = true;
|
||||
console.log("Set Mode: "+mode)
|
||||
// Checks which method was selected
|
||||
if (mode == "serial") {
|
||||
try{
|
||||
// - Request a port and open an asynchronous connection,
|
||||
// which prevents the UI from blocking when waiting for
|
||||
// input, and allows serial to be received by the web page
|
||||
// whenever it arrives.
|
||||
|
||||
port = await navigator.serial.requestPort(); // prompt user to select device connected to a com port
|
||||
// - Wait for the port to open.
|
||||
await port.open({ baudRate: 115200 }); // open the port at the proper supported baud rate
|
||||
|
||||
// create a text encoder output stream and pipe the stream to port.writeable
|
||||
const encoder = new TextEncoderStream();
|
||||
outputDone = encoder.readable.pipeTo(port.writable);
|
||||
outputStream = encoder.writable;
|
||||
|
||||
// To put the system into a known state and stop it from echoing back the characters that we send it,
|
||||
// we need to send a CTRL-C and turn off the echo
|
||||
writeToStream('\x03', 'echo(false);');
|
||||
|
||||
// Create an input stream and a reader to read the data. port.readable gets the readable stream
|
||||
// DCC++ commands are text, so we will pipe it through a text decoder.
|
||||
let decoder = new TextDecoderStream();
|
||||
inputDone = port.readable.pipeTo(decoder.writable);
|
||||
inputStream = decoder.readable
|
||||
// .pipeThrough(new TransformStream(new LineBreakTransformer())); // added this line to pump through transformer
|
||||
//.pipeThrough(new TransformStream(new JSONTransformer()));
|
||||
|
||||
// get a reader and start the non-blocking asynchronous read loop to read data from the stream.
|
||||
reader = inputStream.getReader();
|
||||
readLoop();
|
||||
uiDisable(false)
|
||||
displayLog("[CONNECTION] Serial connected")
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log("User didn't select a port to connect to")
|
||||
return false;
|
||||
}
|
||||
} else{
|
||||
// If using the emulator
|
||||
emulatorMode = true;
|
||||
// Displays dummy hardware message
|
||||
displayLog("\n[CONNECTION] Emulator connected")
|
||||
displayLog("[RECEIVE] DCC++ EX COMMAND STATION FOR EMULATOR / EMULATOR MOTOR SHIELD: V-1.0.0 / Feb 30 2020 13:10:04")
|
||||
uiDisable(false)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// While there is still data in the serial buffer us an asynchronous read loop
|
||||
// to get the data and place it in the "value" variable. When "done" is true
|
||||
// all the data has been read or the port is closed
|
||||
async function readLoop() {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
// if (value && value.button) { // alternate check and calling a function
|
||||
// buttonPushed(value);
|
||||
if (value) {
|
||||
displayLog('[RECEIVE] '+value);
|
||||
console.log('[RECEIVE] '+value);
|
||||
}
|
||||
if (done) {
|
||||
console.log('[readLoop] DONE'+done.toString());
|
||||
reader.releaseLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeToStream(...lines) {
|
||||
// Stops data being written to nonexistent port if using emulator
|
||||
let stream = emulatorClass
|
||||
if (port) {
|
||||
stream = outputStream.getWriter();
|
||||
}
|
||||
|
||||
lines.forEach((line) => {
|
||||
if (line == "\x03" || line == "echo(false);") {
|
||||
|
||||
} else {
|
||||
displayLog('[SEND]'+line.toString());
|
||||
}
|
||||
const packet = `<${line}>\n`;
|
||||
stream.write(packet)
|
||||
console.log(packet)
|
||||
});
|
||||
}
|
||||
|
||||
// Transformer for the Web Serial API. Data comes in as a stream so we
|
||||
// need a container to buffer what is coming from the serial port and
|
||||
// parse the data into separate lines by looking for the breaks
|
||||
class LineBreakTransformer {
|
||||
constructor() {
|
||||
// A container for holding stream data until it sees a new line.
|
||||
this.container = '';
|
||||
}
|
||||
|
||||
transform(chunk, controller) {
|
||||
// Handle incoming chunk
|
||||
this.container += chunk; // add new data to the container
|
||||
const lines = this.container.split('\r\n'); // look for line breaks and if it finds any
|
||||
this.container = lines.pop(); // split them into an array
|
||||
lines.forEach(line => controller.enqueue(line)); // iterate parsed lines and send them
|
||||
|
||||
}
|
||||
|
||||
flush(controller) {
|
||||
// When the stream is closed, flush any remaining data
|
||||
controller.enqueue(this.container);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Optional transformer for use with the web serial API
|
||||
// to parse a JSON file into its component commands
|
||||
class JSONTransformer {
|
||||
transform(chunk, controller) {
|
||||
// Attempt to parse JSON content
|
||||
try {
|
||||
controller.enqueue(JSON.parse(chunk));
|
||||
} catch (e) {
|
||||
//displayLog(chunk.toString());
|
||||
//displayLog(chunk);
|
||||
console.log('No JSON, dumping the raw chunk', chunk);
|
||||
controller.enqueue(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async function disconnectServer() {
|
||||
if ($("#power-switch").is(':checked')) {
|
||||
$("#log-box").append('<br>'+'turn off power'+'<br>');
|
||||
writeToStream('0');
|
||||
$("#power-switch").prop('checked', false)
|
||||
$("#power-status").html('Off');
|
||||
}
|
||||
uiDisable(true)
|
||||
if (port) {
|
||||
// Close the input stream (reader).
|
||||
if (reader) {
|
||||
await reader.cancel(); // .cancel is asynchronous so must use await to wave for it to finish
|
||||
await inputDone.catch(() => {});
|
||||
reader = null;
|
||||
inputDone = null;
|
||||
console.log('close reader');
|
||||
}
|
||||
|
||||
// Close the output stream.
|
||||
if (outputStream) {
|
||||
await outputStream.getWriter().close();
|
||||
await outputDone; // have to wait for the azync calls to finish and outputDone to close
|
||||
outputStream = null;
|
||||
outputDone = null;
|
||||
console.log('close outputStream');
|
||||
}
|
||||
// Close the serial port.
|
||||
await port.close();
|
||||
port = null;
|
||||
console.log('close port');
|
||||
displayLog("[CONNECTION] Serial disconnected");
|
||||
} else {
|
||||
// Disables emulator
|
||||
emulatorMode = undefined;
|
||||
displayLog("[CONNECTION] Emulator disconnected");
|
||||
}
|
||||
// Allows a new method to be chosen
|
||||
selectMethod.disabled = false;
|
||||
}
|
||||
|
||||
// Connect or disconnect from the command station
|
||||
async function toggleServer(btn) {
|
||||
// If already connected, disconnect
|
||||
if (port || emulatorMode) {
|
||||
await disconnectServer();
|
||||
btn.attr('aria-state','Disconnected');
|
||||
btn.html('<span class="con-ind"></span>Connect DCC++ EX'); //<span id="con-ind"></span>Connect DCC++ EX
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, call the connect() routine when the user clicks the connect button
|
||||
success = await connectServer();
|
||||
// Checks if the port was opened successfully
|
||||
if (success) {
|
||||
btn.attr('aria-state','Connected');
|
||||
btn.html('<span class="con-ind connected"></span>Disconnect DCC++ EX');
|
||||
} else {
|
||||
selectMethod.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Display log of events
|
||||
function displayLog(data){
|
||||
data = data.replace("<"," ");
|
||||
data = data.replace(">"," ");
|
||||
$("#log-box").append("<br>"+data.toString()+"<br>");
|
||||
$("#log-box").scrollTop($("#log-box").prop("scrollHeight"));
|
||||
}
|
||||
|
||||
// Function to generate commands for functions F0 to F4
|
||||
function sendCommandForF0ToF4(fn, opr){
|
||||
setFunCurrentVal(fn,opr);
|
||||
cabval = (128+getFunCurrentVal("f1")*1 + getFunCurrentVal("f2")*2 + getFunCurrentVal("f3")*4 + getFunCurrentVal("f4")*8 + getFunCurrentVal("f0")*16);
|
||||
writeToStream("f "+getCV()+" "+cabval);
|
||||
console.log("Command: "+ "f "+getCV()+" "+cabval);
|
||||
|
||||
}
|
||||
|
||||
// Function to generate commands for functions F5 to F8
|
||||
function sendCommandForF5ToF8(fn, opr){
|
||||
setFunCurrentVal(fn,opr);
|
||||
cabval = (176+getFunCurrentVal("f5")*1 + getFunCurrentVal("f6")*2 + getFunCurrentVal("f7")*4 + getFunCurrentVal("f8")*8);
|
||||
writeToStream("f "+getCV()+" "+cabval);
|
||||
console.log("Command: "+ "f "+getCV()+" "+cabval);
|
||||
|
||||
}
|
||||
|
||||
// Function to generate commands for functions F9 to F12
|
||||
function sendCommandForF9ToF12(fn, opr){
|
||||
setFunCurrentVal(fn,opr);
|
||||
cabval = (160+getFunCurrentVal("f9")*1 + getFunCurrentVal("f10")*2 + getFunCurrentVal("f11")*4 + getFunCurrentVal("f12")*8);
|
||||
writeToStream("f "+getCV()+" "+cabval);
|
||||
console.log("Command: "+ "f "+getCV()+" "+cabval);
|
||||
|
||||
}
|
||||
|
||||
// Function to generate commands for functions F13 to F20
|
||||
function sendCommandForF13ToF20(fn, opr){
|
||||
setFunCurrentVal(fn,opr);
|
||||
cabval = (getFunCurrentVal("f13")*1 + getFunCurrentVal("f14")*2 + getFunCurrentVal("f15")*4 + getFunCurrentVal("f16")*8 + getFunCurrentVal("f17")*16 + getFunCurrentVal("f18")*32 + getFunCurrentVal("f19")*64 + getFunCurrentVal("f20")*128);
|
||||
writeToStream("f "+getCV()+" 222 "+cabval);
|
||||
console.log("Command: "+ "f "+getCV()+" 222 "+cabval);
|
||||
|
||||
}
|
||||
|
||||
// Function to generate commands for functions F21 to F28
|
||||
function sendCommandForF21ToF28(fn, opr){
|
||||
setFunCurrentVal(fn,opr);
|
||||
cabval = (getFunCurrentVal("f21")*1 + getFunCurrentVal("f22")*2 + getFunCurrentVal("f23")*4 + getFunCurrentVal("f24")*8 + getFunCurrentVal("f25")*16 + getFunCurrentVal("f26")*32 + getFunCurrentVal("f27")*64 + getFunCurrentVal("f28")*128);
|
||||
writeToStream("f "+getCV()+" 223 "+cabval);
|
||||
console.log("Command: "+ "f "+getCV()+" 223 "+cabval);
|
||||
|
||||
}
|
187
ram/driver/static/wt/js/emulator.js
Normal file
187
ram/driver/static/wt/js/emulator.js
Normal file
@@ -0,0 +1,187 @@
|
||||
/* This is part of the DCC++ EX Project for model railroading and more.
|
||||
For licence information, please see index.html.
|
||||
For more information, see us at dcc-ex.com.
|
||||
|
||||
emulator.js
|
||||
|
||||
Allows the software to operate without a Command Station attached. This
|
||||
file manages the correct response that a Command Station would provide.
|
||||
*/
|
||||
/**
|
||||
* Utility function can be moved when we can import files
|
||||
* @description Removes control characters "<", ">" and "\n"
|
||||
* @param {string} packet
|
||||
* @return {string}
|
||||
*/
|
||||
function removeControlCharacters(packet) {
|
||||
return [...packet].filter(char => !["<", ">", "\n"].includes(char)).join("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function can be moved when we can import files
|
||||
* @description Finds the first non-blank character after control characters are removed
|
||||
* @param {string} packet
|
||||
* @return {string}
|
||||
*/
|
||||
function extractPacketKey(packet) {
|
||||
const cleanedPacket = [...removeControlCharacters(packet)]
|
||||
return cleanedPacket.find(char => char !== " ");
|
||||
}
|
||||
|
||||
class Emulator {
|
||||
constructor({logger}) {
|
||||
this.turnoutEmulator = new TurnoutEmulator()
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
*/
|
||||
write(packet) {
|
||||
if (packet === "<credits>") {
|
||||
console.log("Credits")
|
||||
return credits()
|
||||
}
|
||||
|
||||
let packetKey = extractPacketKey(packet);
|
||||
|
||||
switch (packetKey) {
|
||||
// Cab control
|
||||
case ("t"):
|
||||
this.logger('[RECEIVE] '+ this.#cabControlCommand(packet))
|
||||
break;
|
||||
|
||||
// Track power off
|
||||
case ('0') :
|
||||
this.logger('[RECEIVE] '+ this.#powerOffCommand(packet));
|
||||
break;
|
||||
|
||||
// Track power on
|
||||
case ('1'):
|
||||
this.logger('[RECEIVE] '+ this.#powerOnCommand(packet));
|
||||
break;
|
||||
|
||||
// New cab functions
|
||||
case ('F'):
|
||||
this.logger('[RECEIVE] '+ this.#cabFunctionCommand(packet));
|
||||
break;
|
||||
|
||||
// Legacy cab functions
|
||||
case ('f'):
|
||||
|
||||
this.logger('[RECEIVE] '+ this.#cabFunctionCommand(packet, true));
|
||||
break;
|
||||
|
||||
// Turnouts
|
||||
case ('T'): //Not fully finished
|
||||
this.logger('[RECEIVE] '+ this.#turnoutCommand(packet, this.turnoutEmulator))
|
||||
break
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
* @return {string}
|
||||
*/
|
||||
#cabControlCommand(packet) {
|
||||
const splitPacket = packet.split(" ");
|
||||
return 'T 1 ' + splitPacket[3] + ' ' + splitPacket[4].substring(0, splitPacket[4].length - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
* @return {string}
|
||||
*/
|
||||
#powerOffCommand(packet) {
|
||||
return 'p0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
* @return {string}
|
||||
*/
|
||||
#powerOnCommand(packet) {
|
||||
return 'p1';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
* @param {boolean} legacy
|
||||
* @return number
|
||||
*/
|
||||
#cabFunctionCommand(packet, legacy = false) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packet
|
||||
* @param {TurnoutEmulator} turnoutEmulator
|
||||
* @return {string}
|
||||
*/
|
||||
#turnoutCommand(packet, turnoutEmulator = new TurnoutEmulator()) {
|
||||
const splitPacket = removeControlCharacters(packet).split(" ");
|
||||
|
||||
if (splitPacket.length === 4) {
|
||||
// Adds a Turnout
|
||||
return turnoutEmulator.addTurnout(new Turnout(packet))
|
||||
} else if (splitPacket.length === 2) {
|
||||
// Removes a Turnout
|
||||
return turnoutEmulator.removeTurnout(splitPacket[1])
|
||||
} else if (splitPacket.length === 1) {
|
||||
// Reads Turnouts
|
||||
return turnoutEmulator.turnouts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TurnoutEmulator {
|
||||
/**
|
||||
* @type {[]}
|
||||
*/
|
||||
#turnouts = []
|
||||
|
||||
/**
|
||||
* @return {string|string[]}
|
||||
*/
|
||||
get turnouts() {
|
||||
if (!this.#turnouts.length) {
|
||||
return 'X'
|
||||
}
|
||||
|
||||
return this.#turnouts.map(turnout => `H ${turnout.id} ${turnout.address} ${turnout.subaddress} ${turnout.thrown}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} turnout
|
||||
* @return {string}
|
||||
*/
|
||||
addTurnout(turnout) {
|
||||
this.#turnouts = [...this.#turnouts, turnout]
|
||||
console.log(this.#turnouts);
|
||||
return '<O>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} turnoutId
|
||||
* @return {string}
|
||||
*/
|
||||
removeTurnout(turnoutId) {
|
||||
this.#turnouts = this.#turnouts.filter(turnout => turnout.id !== turnoutId);
|
||||
console.log(this.#turnouts);
|
||||
return 'O';
|
||||
}
|
||||
}
|
||||
|
||||
class Turnout {
|
||||
constructor(packet) {
|
||||
let [_key, id, address, subaddress] = removeControlCharacters(packet).split(" ");
|
||||
|
||||
this.id = id
|
||||
this.address = address
|
||||
this.subaddress = subaddress
|
||||
this.thrown = 0
|
||||
}
|
||||
}
|
971
ram/driver/static/wt/js/exwebthrottle.js
Normal file
971
ram/driver/static/wt/js/exwebthrottle.js
Normal file
@@ -0,0 +1,971 @@
|
||||
/*
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or$("#log-box").append("<br>"+data+"<br>");
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Authors: Fred Decker
|
||||
Mani Kumar
|
||||
Matt
|
||||
|
||||
This is part of the DCC++ EX Project for model railroading and more.
|
||||
For more information, see us at dcc-ex.com.
|
||||
*/
|
||||
window.cv=0;
|
||||
window.speed=0;
|
||||
window.direction=1;
|
||||
window.server="";
|
||||
window.port=4444;
|
||||
window.speedStep = 1;
|
||||
window.functions = {
|
||||
"f0": 0,
|
||||
"f1": 0,
|
||||
"f2": 0,
|
||||
"f3": 0,
|
||||
"f4": 0,
|
||||
"f5": 0,
|
||||
"f6": 0,
|
||||
"f7": 0,
|
||||
"f8": 0,
|
||||
"f9": 0,
|
||||
"f10": 0,
|
||||
"f11": 0,
|
||||
"f12": 0,
|
||||
"f13": 0,
|
||||
"f14": 0,
|
||||
"f15": 0,
|
||||
"f16": 0,
|
||||
"f17": 0,
|
||||
"f18": 0,
|
||||
"f19": 0,
|
||||
"f20": 0,
|
||||
"f21": 0,
|
||||
"f22": 0,
|
||||
"f23": 0,
|
||||
"f24": 0,
|
||||
"f25": 0,
|
||||
"f26": 0,
|
||||
"f27": 0,
|
||||
"f28": 0
|
||||
};
|
||||
window.isStopped = true;
|
||||
let port;
|
||||
let emulatorMode;
|
||||
let reader;
|
||||
let inputDone;
|
||||
let outputDone;
|
||||
let inputStream;
|
||||
let outputStream;
|
||||
|
||||
let pressed = false;
|
||||
|
||||
|
||||
// Enables and disables ui elements
|
||||
|
||||
function uiDisable (status) {
|
||||
/*document.getElementById('ex-locoid').disabled = status
|
||||
document.getElementById('power-switch').disabled = status
|
||||
document.getElementById('button-sendCmd').disabled = status
|
||||
document.getElementById('dir-f').disabled = status
|
||||
document.getElementById('dir-S').disabled = status*/
|
||||
//document.getElementById('dir-b').disabled = status
|
||||
$("#ex-locoid").prop('disabled', status)
|
||||
$("#power-switch").prop('disabled', status)
|
||||
$("#button-sendCmd").prop('disabled', status)
|
||||
$("#dir-f").prop('disabled', status)
|
||||
$("#dir-S").prop('disabled', status)
|
||||
$("#dir-b").prop('disabled', status)
|
||||
if (status){
|
||||
//$("#throttle").roundSlider("disable");
|
||||
//toggleThrottleState(false)
|
||||
$("#button-getloco").trigger("click");
|
||||
} else {
|
||||
//$("#throttle").roundSlider("enable");
|
||||
//toggleThrottleState(true)
|
||||
//$("#button-getloco").trigger("click");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns given function current value (0-disable/1-enable)
|
||||
function getFunCurrentVal(fun){
|
||||
return window.functions[fun];
|
||||
}
|
||||
// Set given function current value with given value (0/1)
|
||||
function setFunCurrentVal(fun, val){
|
||||
window.functions[fun]=val;
|
||||
}
|
||||
// Set given CV value
|
||||
function setCV(val){
|
||||
window.cv = val;
|
||||
console.log("Set Cab Address: "+val);
|
||||
}
|
||||
// Get stored CV value
|
||||
function getCV(){
|
||||
return window.cv
|
||||
}
|
||||
|
||||
// Set Speed value
|
||||
function setSpeed(sp){
|
||||
window.speed=sp;
|
||||
}
|
||||
|
||||
// Get Speed value
|
||||
function getSpeed(){
|
||||
return parseInt(window.speed);
|
||||
}
|
||||
|
||||
// Set Direction
|
||||
function setDirection(dir){
|
||||
window.direction=dir;
|
||||
}
|
||||
|
||||
// Get Direction value
|
||||
function getDirection(dir){
|
||||
return window.direction;
|
||||
}
|
||||
|
||||
// Handling Disabling functionality for Knob Type throttle (it will not Out of the box)
|
||||
function toggleKnobState(ele, state) {
|
||||
if(!state){
|
||||
ele.addClass("disabled");
|
||||
}else{
|
||||
ele.removeClass("disabled");
|
||||
}
|
||||
}
|
||||
function loadMapData(map){
|
||||
data = [];
|
||||
if (map == "Default") {
|
||||
data = { mname: "Default", fnData: fnMasterData };
|
||||
} else {
|
||||
data = getStoredMapData(map);
|
||||
}
|
||||
$("#mapping-panel").empty();
|
||||
$("#mapping-panel").append(
|
||||
'<div class="settings-subheading row">' +
|
||||
'<div class="column-7 pl0"><div class="map-name-heading">' +
|
||||
map +
|
||||
"</div></div>" +
|
||||
'<div class="column-3 pr0">' +
|
||||
'<input type="hidden" id="cur-map-val" cur-map="'+map+'"/>' +
|
||||
'<a href="#" class="option-btn waste-bin" id="delete-map"> 🗑</a>' +
|
||||
'<a href="#" class="option-btn" id="import-map"> ↓</a>' +
|
||||
'<a href="#" class="option-btn" id="export-cur-map"> ↑</a>' +
|
||||
'<a href="#" class="option-btn" id="edit-cur-map">✎</a>' +
|
||||
"</div>" +
|
||||
"</div>"
|
||||
);
|
||||
$("#mapping-panel").append(
|
||||
'<div class="row settings-group fnhead">' +
|
||||
'<div class="column-2">Function</div>'+
|
||||
'<div class="column-4">Name</div>'+
|
||||
'<div class="column-2">Type</div>' +
|
||||
'<div class="column-2">Visible</div>' +
|
||||
'</div>'
|
||||
);
|
||||
container = $('<div class="maps-content"></div>').appendTo("#mapping-panel");
|
||||
$.each(data.fnData, function (key, value) {
|
||||
container.append(
|
||||
'<div class="row settings-group" id="'+key+'">' +
|
||||
'<div class="column-2 caplitalize">'+key+'</div>'+
|
||||
'<div class="column-4">'+value[2]+'</div>'+
|
||||
'<div class="column-2">'+ (value[1] == 1 ? '<span class="pill red">Momentary</span>' : '<span class="pill green">Latching</span>') +'</div>' +
|
||||
'<div class="column-2">'+ (value[3] == 1 ? '<span class="pill green">Visible</span>' : '<span class="pill">Hidden</span>') +'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
function loadLocomotives(){
|
||||
locos = getLocoList();
|
||||
$("#locomotives-panel").empty();
|
||||
$.each(locos, function (key, value) {
|
||||
$("#locomotives-panel").append(
|
||||
'<div class="row settings-group" id="'+key+'">'+
|
||||
'<div class="column-1 sno"><p>' + (key + 1) + "</p></div>" +
|
||||
'<div class="column-7 loco-details">' +
|
||||
'<div class="row">' +
|
||||
'<div class="column-7"><p class="ac-loco-name column-10">' +
|
||||
value.name +
|
||||
"</p></div>" +
|
||||
'<div class="column-2 cv-num"><p><small>CV </small>' +
|
||||
value.cv +
|
||||
"</p></div>" +
|
||||
"</div>" +
|
||||
'<div class="row sub-text">' +
|
||||
'<div class="column-3"><p>' + value.type + '</p></div>' +
|
||||
'<div class="column-3">' +
|
||||
(value.decoder == "" ? '<p class="nd">Undefined</p>' : "<p>" + value.decoder + "</p>") +
|
||||
"</div>" +
|
||||
'<div class="column-3">' +
|
||||
(value.brand == "" ? '<p class="nd">Undefined</p>' : "<p>" + value.brand + "</p>") +
|
||||
"</div></div></div>" +
|
||||
'<div class="column-2 asst-map"><div class="row">' +
|
||||
'<div class="column-7"><p class="muted">Map</p><p>' +
|
||||
value.map +
|
||||
"</p></div>" +
|
||||
'<div class="column-3 prh"><a href="#" loco-id="'+key+'" data-loco="'+
|
||||
value.name+'" class="edit-cur-loco"> ✎ </a></div></div>' +
|
||||
"</div></br>"
|
||||
);
|
||||
});
|
||||
}
|
||||
//Initialization routine for Throttle screen
|
||||
function setThrottleScreenUI() {
|
||||
loadmaps();
|
||||
loadButtons({ mname: "default", fnData: fnMasterData });
|
||||
controller = getPreference("scontroller");
|
||||
$("#throttle-selector").val(controller).trigger("change");
|
||||
setspeedControllerType(controller);
|
||||
|
||||
// Show and hide debug console based on preference set in earlier session
|
||||
if (getPreference("dbugConsole") == null) {
|
||||
setPreference("dbugConsole", true);
|
||||
}
|
||||
if(getPreference("dbugConsole")) {
|
||||
$("#debug-console").show()
|
||||
$("#console-toggle").prop("checked", true);
|
||||
}else{
|
||||
$("#debug-console").hide();
|
||||
$("#console-toggle").prop("checked", false);
|
||||
}
|
||||
|
||||
$(".dir-toggle").addClass("forward");
|
||||
|
||||
// Set theme
|
||||
if (getPreference("theme") == null) {
|
||||
setPreference("theme", "simple");
|
||||
}else{
|
||||
theme = getPreference("theme");
|
||||
$("#theme-selector").val(theme).trigger("change");
|
||||
if (theme != "simple") {
|
||||
$("link[title*='theme']").remove();
|
||||
$("head").append(
|
||||
'<link rel="stylesheet" type="text/css" title="theme" href="css/themes/'+theme+'.css">'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the Speed controller type
|
||||
function setspeedControllerType(pref){
|
||||
// Set saved throttle or use circular throttle as default
|
||||
$(".speedController").hide();
|
||||
switch (pref) {
|
||||
case "vertical":
|
||||
console.log("Vertical Speed Controller");
|
||||
$("#vertical-throttle").show();
|
||||
break;
|
||||
case "knob":
|
||||
console.log("Knob Speed Controller");
|
||||
$("#knobthrottle").show();
|
||||
break;
|
||||
case "circular":
|
||||
console.log("Circular Speed Controller");
|
||||
$("#circular-throttle").show();
|
||||
break;
|
||||
case "default":
|
||||
case null:
|
||||
case undefined:
|
||||
console.log("Fallback Speed Controller");
|
||||
$("#vertical-throttle").show();
|
||||
setPreference("scontroller", "vertical");
|
||||
$("#throttle-selector").val("vertical").trigger("change");
|
||||
}
|
||||
}
|
||||
|
||||
// Enabling/disabling Speed Controllers
|
||||
function toggleThrottleState(state){
|
||||
if(state){
|
||||
$("#circular-throttle").roundSlider("enable");
|
||||
$("#v-throttle").slider("enable");
|
||||
toggleKnobState($("#knobthrottle"), true);
|
||||
}else{
|
||||
$("#circular-throttle").roundSlider("disable");
|
||||
$("#v-throttle").slider("disable");
|
||||
toggleKnobState($("#knobthrottle"), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* This function will
|
||||
1. Send Speed command
|
||||
2. Set speed value of all the available sliders/controllers
|
||||
3. Set respctive speed number
|
||||
************************************************************/
|
||||
function setSpeedofControllers(){
|
||||
spd = getSpeed();
|
||||
|
||||
if(!isStopped){
|
||||
writeToStream("t 01 " + getCV() + " " + spd + " " + getDirection());
|
||||
}
|
||||
// Circular
|
||||
$("#circular-throttle").roundSlider("setValue", spd);
|
||||
|
||||
// Vertical
|
||||
$("#v-throttle").val(spd).change();
|
||||
$("#v-throttle").slider("option", "value", spd);
|
||||
$("#speed-indicator").html(spd);
|
||||
|
||||
// Knob
|
||||
$("#knob-value").html(spd);
|
||||
knob.val(spd).change();
|
||||
}
|
||||
|
||||
// This function will generate commands for each type of function
|
||||
function generateFnCommand(clickedBtn){
|
||||
|
||||
func = clickedBtn.attr('name'); // Gives function name (F1, F2, .... F28)
|
||||
eventType = clickedBtn.data("type"); // Gives type of button (Press/Hold or Toggle)
|
||||
btnPressed = clickedBtn.attr("aria-pressed");
|
||||
//console.log("Function Name=>"+func+" , Button Type=>"+eventType+" , Button Pressed=>"+btnStatus);
|
||||
|
||||
switch(func){
|
||||
case "f0":
|
||||
case "f1":
|
||||
case "f2":
|
||||
case "f3":
|
||||
case "f4":
|
||||
{
|
||||
if(btnPressed=="true"){
|
||||
sendCommandForF0ToF4(func,1);
|
||||
}else{
|
||||
sendCommandForF0ToF4(func,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "f5":
|
||||
case "f6":
|
||||
case "f7":
|
||||
case "f8":
|
||||
{
|
||||
if(btnPressed=="true"){
|
||||
sendCommandForF5ToF8(func,1);
|
||||
}else{
|
||||
sendCommandForF5ToF8(func,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "f9":
|
||||
case "f10":
|
||||
case "f11":
|
||||
case "f12":
|
||||
{
|
||||
if(btnPressed=="true"){
|
||||
sendCommandForF9ToF12(func,1);
|
||||
}else{
|
||||
sendCommandForF9ToF12(func,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "f13":
|
||||
case "f14":
|
||||
case "f15":
|
||||
case "f16":
|
||||
case "f17":
|
||||
case "f18":
|
||||
case "f19":
|
||||
case "f20":
|
||||
{
|
||||
if(btnPressed=="true"){
|
||||
sendCommandForF13ToF20(func,1);
|
||||
}else{
|
||||
sendCommandForF13ToF20(func,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "f21":
|
||||
case "f22":
|
||||
case "f23":
|
||||
case "f24":
|
||||
case "f25":
|
||||
case "f26":
|
||||
case "f27":
|
||||
case "f28":
|
||||
{
|
||||
if(btnPressed=="true"){
|
||||
sendCommandForF21ToF28(func,1);
|
||||
}else{
|
||||
sendCommandForF21ToF28(func,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
alert("Invalid Function");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
var mode = 0;
|
||||
// Left Menu
|
||||
$("#nav-open").on("click", function () {
|
||||
$("#side-menu").show().animate({ left: 0 });
|
||||
});
|
||||
$("#nav-close").on("click", function () {
|
||||
$("#side-menu").animate({ left: -260 }, function(){
|
||||
$("#side-menu").hide();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
$("#info-tooltip").tooltip({
|
||||
content:
|
||||
"<p>DCC++ EX Web Throttle<br>(WebThrottle-EX)</p><hr><p>Version: "+version+"</p><p><b>Credits</b><br> Fred Decker <br> Mani Kumar <br> Matt H</p>",
|
||||
show: {
|
||||
effect: "slideDown",
|
||||
delay: 100,
|
||||
},
|
||||
classes: {
|
||||
"ui-tooltip": "credits-tooltip",
|
||||
},
|
||||
position: {
|
||||
my: "left top",
|
||||
at: "left bottom",
|
||||
},
|
||||
});
|
||||
|
||||
// Load function map, buttons throttle etc
|
||||
setThrottleScreenUI();
|
||||
$("#throttle-selector").on("change", function (e) {
|
||||
selectedval = $(this).val();
|
||||
console.log(selectedval);
|
||||
setPreference("scontroller", selectedval);
|
||||
setspeedControllerType(selectedval);
|
||||
});
|
||||
|
||||
$("#theme-selector").on("change", function (e) {
|
||||
selectedval = $(this).val();
|
||||
console.log(selectedval);
|
||||
setPreference("theme", selectedval);
|
||||
$("link[title*='theme']").remove();
|
||||
if (selectedval != "simple") {
|
||||
$("head").append(
|
||||
'<link rel="stylesheet" type="text/css" title="theme" href="css/themes/' +
|
||||
selectedval +
|
||||
'.css">'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Connect command station
|
||||
$("#button-connect").on("click", function () {
|
||||
toggleServer($(this));
|
||||
});
|
||||
|
||||
// Disconnect command station
|
||||
$("#button-disconnect").on("click", function () {
|
||||
disconnectServer();
|
||||
});
|
||||
|
||||
// Aquire loco of given CV
|
||||
$("#button-getloco").on("click", function () {
|
||||
acButton = $(this);
|
||||
isAcquired = $(this).data("acquired");
|
||||
// Parse int only returns number if the string is starting with Number
|
||||
locoid_input = parseInt($("#ex-locoid").val());
|
||||
|
||||
if (locoid_input != 0) {
|
||||
if (isAcquired == false && getCV() == 0) {
|
||||
setCV(locoid_input);
|
||||
$("#loco-info").html("Acquired Locomotive: " + locoid_input);
|
||||
acButton.data("acquired", true);
|
||||
acButton.html('<span class="icon-cross"></span>');
|
||||
toggleThrottleState(true);
|
||||
} else {
|
||||
currentCV = getCV();
|
||||
$("#ex-locoid").val(0);
|
||||
setCV(0);
|
||||
$("#loco-info").html("Released Locomotive: " + currentCV);
|
||||
acButton.data("acquired", false);
|
||||
acButton.html('<span class="icon-circle-right"></span>');
|
||||
toggleThrottleState(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Switch ON/OFF power of the Command station
|
||||
$("#power-switch").on("click", function () {
|
||||
pb = $(this).is(":checked");
|
||||
|
||||
if (pb == true) {
|
||||
writeToStream("1");
|
||||
$("#power-status").html("On");
|
||||
} else {
|
||||
writeToStream("0");
|
||||
$("#power-status").html("Off");
|
||||
}
|
||||
});
|
||||
////////////////////////////////////
|
||||
$("#v-throttle").slider({
|
||||
orientation: "vertical",
|
||||
min: 0,
|
||||
max: 126,
|
||||
disabled: true,
|
||||
range: "max",
|
||||
slide: function (event, ui) {
|
||||
$("#speed-indicator").html(ui.value);
|
||||
setSpeed(ui.value);
|
||||
setSpeedofControllers();
|
||||
},
|
||||
});
|
||||
|
||||
/////////////////////////////////////////*/
|
||||
knob = $(".rotarySwitch").rotaryswitch({
|
||||
minimum: 0,
|
||||
maximum: 126,
|
||||
step: 2,
|
||||
beginDeg: 210,
|
||||
lengthDeg: 295,
|
||||
minimumOverMaximum: true,
|
||||
showMarks: true,
|
||||
themeClass: "big light",
|
||||
});
|
||||
toggleKnobState($("#knobthrottle"), false);
|
||||
knob.on("change", function () {
|
||||
oldValue = getSpeed();
|
||||
kval = knob.val();
|
||||
$("#knob-value").html(kval);
|
||||
setSpeed(kval);
|
||||
// Below condition is to avoid infinite loop
|
||||
// that triggers change() event indifinitely
|
||||
if (oldValue != kval) {
|
||||
setSpeedofControllers();
|
||||
} else {
|
||||
writeToStream(
|
||||
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
|
||||
);
|
||||
}
|
||||
//console.log( "t 01 " + getCV() + " " + getSpeed() + " " + getDirection());
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Speed (round) Slider allows user to change the speed of the locomotive
|
||||
Tht = $("#circular-throttle").roundSlider({
|
||||
width: 20,
|
||||
radius: 116,
|
||||
value: speed,
|
||||
circleShape: "pie",
|
||||
handleShape: "dot",
|
||||
startAngle: 315,
|
||||
lineCap: "round",
|
||||
sliderType: "min-range",
|
||||
showTooltip: true,
|
||||
editableTooltip: false,
|
||||
handleSize: "+18",
|
||||
max: "126",
|
||||
disabled: true,
|
||||
update: function (slider) {
|
||||
setSpeed(slider.value);
|
||||
setSpeedofControllers();
|
||||
//console.log("t 01 "+getCV()+" "+getSpeed()+" "+getDirection());
|
||||
},
|
||||
valueChange: function (slider) {
|
||||
//setSpeed(slider.value);
|
||||
//writeToStream("t 01 "+getCV()+" "+getSpeed()+" "+getDirection());
|
||||
// console.log("This event is similar to 'update' event, in addition it will trigger even the value was changed through programmatically also.");
|
||||
},
|
||||
});
|
||||
|
||||
// Allows user to change the direction of the loco and STOP.
|
||||
$(".dir-btn").on("click", function () {
|
||||
if (getCV() != 0){
|
||||
current = $(this);
|
||||
dir = current.attr("aria-label");
|
||||
$(".dir-btn").removeClass("selected");
|
||||
current.addClass("selected", 200);
|
||||
console.log(dir);
|
||||
$(".dir-toggle").removeClass("forward backward stop");
|
||||
$(".dir-toggle").addClass(dir);
|
||||
// Do direction stuff here
|
||||
switch (dir) {
|
||||
case "forward": {
|
||||
isStopped = false;
|
||||
setDirection(1);
|
||||
setSpeedofControllers();
|
||||
writeToStream("t 01 " + getCV() + " " + getSpeed() + " 1");
|
||||
break;
|
||||
}
|
||||
case "backward": {
|
||||
isStopped = false;
|
||||
setDirection(0);
|
||||
setSpeedofControllers();
|
||||
writeToStream("t 01 " + getCV() + " " + getSpeed() + " 0");
|
||||
break;
|
||||
}
|
||||
case "stop": {
|
||||
isStopped = true;
|
||||
dir = getDirection();
|
||||
setSpeed(0);
|
||||
setSpeedofControllers();
|
||||
writeToStream("t 01 " + getCV() + " 0 " + dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
console.log("No loco acquired");
|
||||
}
|
||||
});
|
||||
|
||||
$("#emergency-stop").on("click", function () {
|
||||
if (getCV() != 0){
|
||||
isStopped = true;
|
||||
dir = getDirection();
|
||||
setSpeed(0);
|
||||
setSpeedofControllers();
|
||||
writeToStream("t 01 " + getCV() + " -1 " + dir);
|
||||
}
|
||||
else{
|
||||
console.log("No loco acquired");
|
||||
}
|
||||
});
|
||||
|
||||
// Hide/Show the Loco, Connect server fields (on top)
|
||||
$("#button-hide").on("click", function () {
|
||||
if ($(".details-panel").is(":visible")) {
|
||||
$(".details-panel").hide();
|
||||
$(this).css("top", 0);
|
||||
$(this).html('<span class="icon-circle-down"></span>');
|
||||
} else {
|
||||
$(".details-panel").show();
|
||||
$(this).html('<span class="icon-circle-up"></span>');
|
||||
$(this).css("top", "-9px");
|
||||
}
|
||||
});
|
||||
|
||||
// PLUS button. Increases speed on Hold / Tap
|
||||
var tId = 0;
|
||||
$("#button-right")
|
||||
.on("mousedown", function () {
|
||||
event.stopImmediatePropagation();
|
||||
tId = setInterval(function () {
|
||||
var sp = getSpeed();
|
||||
if (sp <= 125 && getDirection() != -1 && getCV() != 0) {
|
||||
setSpeed(sp + speedStep);
|
||||
setSpeedofControllers();
|
||||
writeToStream(
|
||||
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
|
||||
);
|
||||
sp = 0;
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.on("mouseup mouseleave", function () {
|
||||
clearInterval(tId);
|
||||
})
|
||||
.on("click", function () {
|
||||
event.stopImmediatePropagation();
|
||||
var sp = getSpeed();
|
||||
if (sp <= 125 && getDirection() != -1 && getCV() != 0) {
|
||||
setSpeed(sp + speedStep);
|
||||
setSpeedofControllers();
|
||||
writeToStream(
|
||||
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
|
||||
);
|
||||
sp = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// MINUS button. Decreases speed on Hold / Tap
|
||||
var tId = 0;
|
||||
$("#button-left")
|
||||
.on("mousedown", function () {
|
||||
event.stopImmediatePropagation();
|
||||
tId = setInterval(function () {
|
||||
var sp = getSpeed(sp);
|
||||
if (sp >= 1 && getDirection() != -1 && getCV() != 0) {
|
||||
setSpeed(sp - speedStep);
|
||||
setSpeedofControllers();
|
||||
writeToStream(
|
||||
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
|
||||
);
|
||||
sp = 0;
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.on("mouseup mouseleave", function () {
|
||||
clearInterval(tId);
|
||||
})
|
||||
.on("click", function () {
|
||||
event.stopImmediatePropagation();
|
||||
var sp = getSpeed(sp);
|
||||
if (sp >= 1 && getDirection() != -1 && getCV() != 0) {
|
||||
setSpeed(sp - speedStep);
|
||||
setSpeedofControllers();
|
||||
writeToStream(
|
||||
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
|
||||
);
|
||||
sp = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Functions buttons
|
||||
// Send Instructions to generate command depends the type of Button (press/toggle)
|
||||
var timer = 0;
|
||||
$(document)
|
||||
.on("mousedown", ".fn-btn", function () {
|
||||
console.log($(this).val);
|
||||
clickedBtn = $(this);
|
||||
btnType = clickedBtn.data("type");
|
||||
if (btnType == "press") {
|
||||
timer = setInterval(function () {
|
||||
// MOMENTARY HOLD ON
|
||||
clickedBtn.attr("aria-pressed", "true");
|
||||
generateFnCommand(clickedBtn);
|
||||
console.log("PRESSED HOLD ==> " + clickedBtn.attr("name"));
|
||||
}, 100);
|
||||
}
|
||||
})
|
||||
.on("mouseup mouserelease", ".fn-btn", function () {
|
||||
clearInterval(timer);
|
||||
clickedBtn = $(this);
|
||||
btnType = clickedBtn.data("type");
|
||||
btnState = clickedBtn.attr("aria-pressed");
|
||||
if (btnType == "press") {
|
||||
// MOMENTARY HOLD OFF
|
||||
clickedBtn.attr("aria-pressed", "false");
|
||||
generateFnCommand(clickedBtn);
|
||||
console.log("RELEASED HOLD ==> " + clickedBtn.attr("name"));
|
||||
} else {
|
||||
if (btnState == "false") {
|
||||
// TOGGLE ON
|
||||
clickedBtn.attr("aria-pressed", "true");
|
||||
generateFnCommand(clickedBtn);
|
||||
console.log("TOGGLE ON ==> " + clickedBtn.attr("name"));
|
||||
} else {
|
||||
// TOGGLE OFF
|
||||
clickedBtn.attr("aria-pressed", "false");
|
||||
generateFnCommand(clickedBtn);
|
||||
console.log("TOGGLE OFF ==> " + clickedBtn.attr("name"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Hide/Show the Debug console
|
||||
$("#console-toggle").on("click", function () {
|
||||
pb = $(this).is(":checked");
|
||||
if (pb == true) {
|
||||
$("#debug-console").show();
|
||||
setPreference("dbugConsole", true);
|
||||
} else {
|
||||
$("#debug-console").hide();
|
||||
setPreference("dbugConsole", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Send command written in console
|
||||
$("#button-sendCmd").on("click", function () {
|
||||
cmd = $("#cmd-direct").val();
|
||||
writeToStream(cmd);
|
||||
document.getElementById("cmd-direct").value = "";
|
||||
});
|
||||
|
||||
// Clear the console log window
|
||||
$("#button-clearLog").on("click", function () {
|
||||
$("#log-box").html("");
|
||||
});
|
||||
|
||||
// Function to toggle fullScreen viceversa
|
||||
$("#fs-toggle").on("click", function () {
|
||||
st = $(this).attr("state");
|
||||
var elem = document.documentElement;
|
||||
if (st == "ws") {
|
||||
$(this).attr("state", "fs");
|
||||
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
} else if (elem.mozRequestFullScreen) {
|
||||
/* Firefox */
|
||||
elem.mozRequestFullScreen();
|
||||
} else if (elem.webkitRequestFullscreen) {
|
||||
/* Chrome, Safari and Opera */
|
||||
elem.webkitRequestFullscreen();
|
||||
} else if (elem.msRequestFullscreen) {
|
||||
/* IE/Edge */
|
||||
elem.msRequestFullscreen();
|
||||
}
|
||||
} else {
|
||||
$(this).attr("state", "ws");
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
/* Firefox */
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
/* Chrome, Safari and Opera */
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
/* IE/Edge */
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Handles navigation clicks
|
||||
$("#throttle-nav").on("click", function () {
|
||||
hideWindows();
|
||||
$("#throttle-window").show();
|
||||
$("#nav-close").trigger("click");
|
||||
});
|
||||
$("#loco-nav").on("click", function () {
|
||||
hideWindows();
|
||||
$("#loco-window").show();
|
||||
$("#nav-close").trigger("click");
|
||||
loadLocomotives();
|
||||
});
|
||||
$("#fn-map-nav").on("click", function () {
|
||||
hideWindows();
|
||||
$("#fn-map-window").show();
|
||||
$("#nav-close").trigger("click");
|
||||
setFunctionMaps();
|
||||
//loadMapData("Default");
|
||||
});
|
||||
$("#settings-nav").on("click", function () {
|
||||
hideWindows();
|
||||
$("#settings-window").show();
|
||||
$("#nav-close").trigger("click");
|
||||
});
|
||||
|
||||
eventListeners();
|
||||
|
||||
/*
|
||||
$("#settings-general").on('click', function(){
|
||||
hideSettings();
|
||||
$("#general-section").show();
|
||||
});
|
||||
|
||||
$("#settings-storage").on('click', function(){
|
||||
hideSettings();
|
||||
$("#storage-section").show();
|
||||
});*/
|
||||
|
||||
$("#settings-general").on("click", function () {
|
||||
/*var target = $('#general-section');
|
||||
if (target.length) {
|
||||
$('#settings-panel').animate({
|
||||
scrollTop: target.offset().top
|
||||
}, 1000);
|
||||
|
||||
}*/
|
||||
$("#general-section")[0].scrollIntoView(true);
|
||||
});
|
||||
|
||||
$("#settings-storage").on("click", function () {
|
||||
/*var target = $('#storage-section');
|
||||
if (target.length) {
|
||||
$('#settings-panel').animate({
|
||||
scrollTop: target.offset().top
|
||||
}, 1000);
|
||||
|
||||
}*/
|
||||
$("#storage-section")[0].scrollIntoView(true);
|
||||
});
|
||||
|
||||
$('#settings-app').on('click', function() {
|
||||
$("#app-section")[0].scrollIntoView(true);
|
||||
})
|
||||
|
||||
$(document).on("click", ".map-name", function () {
|
||||
loadMapData($(this).attr("map-val"));
|
||||
$("li.map-name").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
});
|
||||
|
||||
// This allows user to delete currently selected Map
|
||||
$(document).on("click", "#delete-map", function () {
|
||||
selectedval = $("#cur-map-val").attr("cur-map");
|
||||
if (selectedval != "Default") {
|
||||
deleteFuncData(selectedval);
|
||||
loadmaps();
|
||||
setFunctionMaps();
|
||||
loadMapData("Default");
|
||||
$("#select-map").val("default").trigger("change");
|
||||
$("li.map-name").removeClass("active");
|
||||
$("li.map-name[map-val= 'Default']").addClass("active");
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", ".edit-cur-loco", function () {
|
||||
cabdata = getStoredLocoData($(this).attr("data-loco"));
|
||||
$("#loco-form")[0].reset();
|
||||
$("#loco-form-content").css("display", "inline-block");
|
||||
$(".add-loco-form .add-loco-head").html("Edit Locomotive");
|
||||
$("#loco-submit").attr("loco-mode", "edit");
|
||||
$("#loco-submit").attr("loco-id", $(this).attr("loco-id"));
|
||||
$.each(cabdata, function (key, value) {
|
||||
$("#loco-form").children().find("#"+key).val(value);
|
||||
if(key=="map"){
|
||||
$("#function-maps").autocomplete("search", value);
|
||||
var menu = $("#function-maps").autocomplete("widget");
|
||||
$(menu[0].children[0]).click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setFunctionMaps() {
|
||||
const defaultMap = {
|
||||
mname: "Default",
|
||||
fnData: {},
|
||||
}
|
||||
const maps = [defaultMap, ...getMapData()];
|
||||
|
||||
$("#function-mappings").empty();
|
||||
maps.forEach(map => {
|
||||
const name = map.mname
|
||||
$("#function-mappings").append(`<li class='map-name' map-val=${name}>${name}</li>`);
|
||||
})
|
||||
}
|
||||
|
||||
function hideWindows(){
|
||||
$("#throttle-window").hide();
|
||||
$("#loco-window").hide();
|
||||
$("#fn-map-window").hide();
|
||||
$("#settings-window").hide();
|
||||
}
|
||||
|
||||
function hideSettings(){
|
||||
$("#general-section").hide();
|
||||
$("#storage-section").hide();
|
||||
}
|
||||
|
||||
function credits() {
|
||||
authors = ["Fred Decker","Mani Kumar","Matt"]
|
||||
displayLog("Credits:")
|
||||
console.log("Credits:")
|
||||
for (i=0; i<authors.length; i++) {
|
||||
displayLog(authors[i])
|
||||
console.log(authors[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function eventListeners(){
|
||||
var cmdDirect = document.getElementById("cmd-direct");
|
||||
var exLocoID = document.getElementById("ex-locoid");
|
||||
cmdDirect.addEventListener("keyup", function(event) {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
// Trigger the button element with a click
|
||||
$('#button-sendCmd').click();
|
||||
}
|
||||
});
|
||||
exLocoID.addEventListener("keyup", function(event) {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
// Trigger the button element with a click
|
||||
$('#button-getloco').click();
|
||||
}
|
||||
})
|
||||
}
|
34
ram/driver/static/wt/js/fnMaster.js
Normal file
34
ram/driver/static/wt/js/fnMaster.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// This is default function definition
|
||||
// New functions are based on this
|
||||
// If no Map is loaded this will load ad default
|
||||
var fnMasterData = {
|
||||
"f0": [0, 0, "Head Light", 1], // Index => 0=state, 1=type (0-Toggle, 1-press),
|
||||
"f1": [0, 1, "Bell" , 1], // 2=Label, 3= Available
|
||||
"f2": [0, 1, "Horn", 1],
|
||||
"f3": [0, 0, "F3" , 1],
|
||||
"f4": [0, 0, "F4", 1],
|
||||
"f5": [0, 0, "F5", 1],
|
||||
"f6": [0, 0, "F6", 1],
|
||||
"f7": [0, 0, "F7", 1],
|
||||
"f8": [0, 0, "F8", 1],
|
||||
"f9": [0, 0, "F9", 1],
|
||||
"f10": [0, 0, "F10", 1],
|
||||
"f11": [0, 0, "F11", 1],
|
||||
"f12": [0, 0, "F12", 1],
|
||||
"f13": [0, 0, "F13", 1],
|
||||
"f14": [0, 0, "F14", 1],
|
||||
"f15": [0, 0, "F15", 1],
|
||||
"f16": [0, 0, "F16", 1],
|
||||
"f17": [0, 0, "F17", 1],
|
||||
"f18": [0, 0, "F18", 1],
|
||||
"f19": [0, 0, "F19", 1],
|
||||
"f20": [0, 0, "F20", 1],
|
||||
"f21": [0, 0, "F21", 1],
|
||||
"f22": [0, 0, "F22", 1],
|
||||
"f23": [0, 0, "F23", 1],
|
||||
"f24": [0, 0, "F24", 1],
|
||||
"f25": [0, 0, "F25", 1],
|
||||
"f26": [0, 0, "F26", 1],
|
||||
"f27": [0, 0, "F27", 1],
|
||||
"f28": [0, 0, "F28", 1],
|
||||
};
|
4
ram/driver/static/wt/js/jquery-3.2.1.min.js
vendored
Normal file
4
ram/driver/static/wt/js/jquery-3.2.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
ram/driver/static/wt/js/jquery-ui.min.js
vendored
Normal file
13
ram/driver/static/wt/js/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
420
ram/driver/static/wt/js/jquery.rotaryswitch.js
Normal file
420
ram/driver/static/wt/js/jquery.rotaryswitch.js
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
Version 1.0.1
|
||||
|
||||
Copyright 2014 Red White Silver GmbH
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
(function($, window, document, undefined) {
|
||||
|
||||
var pluginName = 'rotaryswitch',
|
||||
defaults = {
|
||||
minimum: 0, // Minimal value
|
||||
maximum: 12, // Maximum value
|
||||
step: 1, // Step size
|
||||
snapInMotion: true, // Snap to steps in motion
|
||||
beginDeg: 0, // Start point in deg
|
||||
lengthDeg: 360, // Length in deg
|
||||
minimumOverMaximum: true, // Which value will used, if the the start and the end point at the same deg.
|
||||
showInput: false, // Show input element
|
||||
showMarks: false, // Show deg marks
|
||||
themeClass: 'defaultTheme' // Theme class
|
||||
};
|
||||
|
||||
function Plugin(element, options) {
|
||||
this.element = $(element);
|
||||
this.domElements = {};
|
||||
this.htmlStructure = {
|
||||
wrap: '<div class="rotaryswitchPlugin"></div>',
|
||||
switchButton: '<div class="switch"></div>',
|
||||
overlay: '<div class="overlay"></div>',
|
||||
marks: '<div class="marks"></div>',
|
||||
mark: '<div class="mark"></div>'
|
||||
};
|
||||
this.mousePosition = {x: -1, y: -1};
|
||||
this.switchDeg = 0;
|
||||
this.valueInPercent = 0;
|
||||
this.value = 0;
|
||||
this.steps = 0;
|
||||
this.totalDeg = 0;
|
||||
this.degPerStep = 0;
|
||||
this.lastTriggeredValue = -1;
|
||||
|
||||
this.options = $.extend({}, defaults, options);
|
||||
this._defaults = defaults;
|
||||
this._name = pluginName;
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
Plugin.prototype = {
|
||||
|
||||
/**
|
||||
* Initialze this plugin
|
||||
* See inline comments for more details
|
||||
*
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
|
||||
initialize: function() {
|
||||
|
||||
// Save the needed jquery DOM elements
|
||||
this.domElements = {
|
||||
// Wrap the input element and save the parent as main element and add theme class:
|
||||
main: this.element.wrap(this.htmlStructure.wrap).parent().addClass(this.options.themeClass),
|
||||
switchButton: $(this.htmlStructure.switchButton),
|
||||
overlay: $(this.htmlStructure.overlay)
|
||||
};
|
||||
|
||||
// Append addional dom elements:
|
||||
this.domElements.main.append([this.domElements.switchButton, this.domElements.overlay]);
|
||||
|
||||
// Calculate the length (Maximum - minimum)
|
||||
this.steps = Math.abs(this.options.maximum - this.options.minimum);
|
||||
|
||||
// Calculate the total deg length
|
||||
this.totalDeg = this.options.lengthDeg;
|
||||
|
||||
// Calculate deg per step
|
||||
this.degPerStep = this.totalDeg / this.steps;
|
||||
|
||||
// Listen to some necessary events
|
||||
this.domElements.main.on('mousedown', $.proxy(this.onMouseDown, this));
|
||||
this.domElements.main.on('touchstart', $.proxy(this.onTouchStart, this));
|
||||
this.element.on('change', $.proxy(this.onChangeElementValue, this));
|
||||
|
||||
this.readValueFromInput(); // Get the value from the input element
|
||||
|
||||
this.rotateSwitch(); // Rotate the switch
|
||||
|
||||
// Show marks if wished
|
||||
if (this.options.showMarks === true) {
|
||||
this.renderMarks();
|
||||
}
|
||||
|
||||
// Show the input element if wished
|
||||
if (this.options.showInput === false) {
|
||||
this.element.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds marks for each step
|
||||
*
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
|
||||
renderMarks: function() {
|
||||
var i=0,
|
||||
len = this.steps / this.options.step,
|
||||
deg = this.options.beginDeg,
|
||||
degPerStep = this.degPerStep * this.options.step,
|
||||
marks = $(this.htmlStructure.marks);
|
||||
|
||||
for (; i < len; i += 1) {
|
||||
deg += degPerStep;
|
||||
var mark = $(this.htmlStructure.mark).css({'transform': 'rotate('+deg+'deg) translate(0, -'+ (this.domElements.main.width()/2 + (this.domElements.main.width()*0.1)) +'px)'});
|
||||
marks.append(mark);
|
||||
}
|
||||
|
||||
this.domElements.main.append(marks);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* On mouse down event handler
|
||||
* Save the mouse position (x, y) to the object this.mousePosition
|
||||
* call the method this.startHandling
|
||||
*
|
||||
* @param event jquery mouse event
|
||||
* @return none
|
||||
*/
|
||||
onMouseDown: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.mousePosition.x = event.pageX;
|
||||
this.mousePosition.y = event.pageY;
|
||||
|
||||
this.startHandling();
|
||||
},
|
||||
|
||||
/**
|
||||
* On mouse up event handler
|
||||
* call the method this.stopHandling
|
||||
*
|
||||
* @param event jquery mouse event
|
||||
* @return none
|
||||
*/
|
||||
onMouseUp: function(event) {
|
||||
this.stopHandling();
|
||||
},
|
||||
|
||||
/**
|
||||
* On mouse move event handler
|
||||
* Save the mouse position (x, y) to the object this.mousePosition
|
||||
* call the method this.calculateSwitchDeg
|
||||
* call the method this.calculateValueByDeg
|
||||
* call the method this.rotateSwitch
|
||||
* call the method this.setValueToInput
|
||||
*
|
||||
* @param event jquery mouse event
|
||||
* @return none
|
||||
*/
|
||||
onMousemove: function(event) {
|
||||
event.preventDefault();
|
||||
this.mousePosition.x = event.pageX;
|
||||
this.mousePosition.y = event.pageY;
|
||||
this.calculateSwitchDeg();
|
||||
this.calculateValueByDeg();
|
||||
this.rotateSwitch();
|
||||
this.setValueToInput();
|
||||
},
|
||||
|
||||
/**
|
||||
* On touch start event handler
|
||||
* Identical width this.onMouseDown
|
||||
*
|
||||
* @param event jquery touch event
|
||||
* @return none
|
||||
* @see this.onMouseDown
|
||||
*/
|
||||
onTouchStart: function(event) {
|
||||
event.preventDefault();
|
||||
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
|
||||
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
|
||||
this.startHandling();
|
||||
},
|
||||
|
||||
/**
|
||||
* On touch end event handler
|
||||
* Identical width this.onMouseUp
|
||||
*
|
||||
* @param event jquery touch event
|
||||
* @return none
|
||||
* @see this.onMouseUp
|
||||
*/
|
||||
onTouchEnd: function(event) {
|
||||
this.stopHandling();
|
||||
},
|
||||
|
||||
/**
|
||||
* On touch move event handler
|
||||
* Identical width this.onMousemove
|
||||
*
|
||||
* @param event jquery touch event
|
||||
* @return none
|
||||
* @see this.onMousemove
|
||||
*/
|
||||
onTouchMove: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
|
||||
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
|
||||
|
||||
this.calculateSwitchDeg();
|
||||
this.calculateValueByDeg();
|
||||
this.rotateSwitch();
|
||||
this.setValueToInput();
|
||||
},
|
||||
|
||||
/**
|
||||
* On change input element event handler
|
||||
* If event not triggered by plugin:
|
||||
* Call the method this.readValueFromInput
|
||||
* Call the method this.rotateSwitch
|
||||
*
|
||||
* @param event jquery event
|
||||
* @return none
|
||||
*/
|
||||
onChangeElementValue: function(event) {
|
||||
if (!event.plugin || event.plugin !== this) {
|
||||
this.readValueFromInput();
|
||||
this.rotateSwitch();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered by mouse or touch begin event
|
||||
* Calls some methods
|
||||
* Listen to some events
|
||||
* Add class 'active' to the main DOM element (this.domElements.main)
|
||||
*
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
startHandling: function() {
|
||||
this.calculateSwitchDeg();
|
||||
this.calculateValueByDeg();
|
||||
this.rotateSwitch();
|
||||
this.setValueToInput();
|
||||
|
||||
$(document).on('mouseup', $.proxy(this.onMouseUp, this));
|
||||
$(document).on('mousemove', $.proxy(this.onMousemove, this));
|
||||
$(document).on('touchend', $.proxy(this.onTouchEnd, this));
|
||||
$(document).on('touchmove', $.proxy(this.onTouchMove, this));
|
||||
|
||||
this.domElements.main.addClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered by mouseup or touchend event
|
||||
* Stop listen to some events
|
||||
* Call method this.rotateSwitch
|
||||
* Remove class 'active' from the main DOM element (this.domElements.main)
|
||||
*
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
stopHandling: function() {
|
||||
$(document).off('mouseup', $.proxy(this.onMouseUp, this))
|
||||
.off('mousemove', $.proxy(this.onMousemove, this))
|
||||
.off('touchend', $.proxy(this.onTouchEnd, this))
|
||||
.off('touchmove', $.proxy(this.onTouchMove, this));
|
||||
|
||||
this.rotateSwitch(true);
|
||||
this.domElements.main.removeClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the switch deg by the element position and the mouse position
|
||||
* Stores the switch deg in this.switchDeg
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
calculateSwitchDeg: function() {
|
||||
var offset = this.domElements.main.offset(),
|
||||
radians = Math.atan2(this.mousePosition.x - (offset.left + (this.domElements.main.width()/2)), this.mousePosition.y - (offset.top + (this.domElements.main.height()/2)));
|
||||
|
||||
if (this.mousePosition.x !== -1) {
|
||||
this.switchDeg = (radians * (180 / Math.PI) * -1) + 180;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the value by deg
|
||||
* Stores the in percent in this.valueInPercent
|
||||
* Stores the value in this.value
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
calculateValueByDeg: function() {
|
||||
var range = this.options.maximum - this.options.minimum;
|
||||
|
||||
if (this.switchDeg - this.options.beginDeg > 0) {
|
||||
this.valueInPercent = (this.switchDeg - this.options.beginDeg) / this.totalDeg;
|
||||
} else {
|
||||
this.valueInPercent = (this.switchDeg - this.options.beginDeg + 360) / this.totalDeg;
|
||||
}
|
||||
|
||||
if (this.valueInPercent > 1) {
|
||||
if (this.valueInPercent > (((360 / this.totalDeg)-1) / 2)+1 ) {
|
||||
this.valueInPercent = 0;
|
||||
} else {
|
||||
this.valueInPercent = 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.value = ~~ (((((range * this.valueInPercent) < 0) ? -0.5 : 0.5) + ((range * this.valueInPercent) / this.options.step))) * this.options.step;
|
||||
this.value += this.options.minimum;
|
||||
|
||||
if (this.options.lengthDeg === 360 && (this.value === this.options.minimum || this.value === this.options.maximum)) {
|
||||
if (this.options.minimumOverMaximum === true) {
|
||||
this.value = this.options.minimum;
|
||||
} else {
|
||||
this.value = this.options.maximum;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotate the switch with css transform
|
||||
* snap to the the next rounded value if the parameter snap is true
|
||||
* @param snap boolean
|
||||
* @return none
|
||||
*/
|
||||
rotateSwitch: function(snap) {
|
||||
var deg = 0,
|
||||
exactDeg = (this.valueInPercent * this.totalDeg),
|
||||
roundedDeg = ((this.value / this.steps) * this.totalDeg) - (this.options.minimum * this.degPerStep),
|
||||
difference = Math.abs(Math.abs(exactDeg) - Math.abs(roundedDeg)),
|
||||
rotateString = '';
|
||||
|
||||
if (snap === true || (this.options.snapInMotion === true && difference < this.degPerStep / 6)) {
|
||||
if (roundedDeg + this.options.beginDeg < 360) {
|
||||
deg = (roundedDeg + this.options.beginDeg);
|
||||
} else {
|
||||
deg = roundedDeg + this.options.beginDeg - 360;
|
||||
}
|
||||
} else {
|
||||
if (exactDeg + this.options.beginDeg < 360) {
|
||||
deg = (exactDeg + this.options.beginDeg);
|
||||
} else {
|
||||
deg = exactDeg + this.options.beginDeg - 360;
|
||||
}
|
||||
}
|
||||
|
||||
rotateString = ['rotate(', deg, 'deg)'].join('');
|
||||
this.domElements.switchButton.css({
|
||||
'transform': rotateString,
|
||||
'-webkit-transform': rotateString,
|
||||
'-moz-transform': rotateString,
|
||||
'-o-transform': rotateString,
|
||||
'-ms-transform': rotateString
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Read the valur from the input element
|
||||
* If no value available, this.options.minimum is used
|
||||
* @param snap boolean
|
||||
* @return none
|
||||
*/
|
||||
readValueFromInput: function() {
|
||||
var elementValue = parseInt(this.element.val(), 10);
|
||||
if (isNaN(elementValue) === true) {
|
||||
this.value = this.options.minimum;
|
||||
} else {
|
||||
this.value = Math.max(this.options.minimum, elementValue);
|
||||
this.value = Math.min(this.options.maximum, this.value);
|
||||
}
|
||||
|
||||
this.value -= this.options.minimum;
|
||||
this.valueInPercent = this.value / (this.options.maximum - this.options.minimum);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the value to the input element
|
||||
* and trigger the change event on the input element
|
||||
* @param none
|
||||
* @return none
|
||||
*/
|
||||
setValueToInput: function() {
|
||||
if (this.value !== this.lastTriggeredValue) {
|
||||
this.lastTriggeredValue = this.value;
|
||||
this.element.val(this.value).trigger({type: 'change', plugin: this});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function(options) {
|
||||
return this.each(function() {
|
||||
if (!$.data(this, 'plugin_' + pluginName)){
|
||||
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}(jQuery, window, document));
|
34
ram/driver/static/wt/js/pwa.js
Normal file
34
ram/driver/static/wt/js/pwa.js
Normal file
@@ -0,0 +1,34 @@
|
||||
$(document).ready(function(){
|
||||
let deferredPrompt;
|
||||
const addBtn = document.querySelector('.add-button');
|
||||
const removeBtn = document.querySelector('.installed-label');
|
||||
addBtn.style.display = 'none';
|
||||
removeBtn.style.display = 'block';
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
removeBtn.style.display = 'none';
|
||||
// Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||
e.preventDefault();
|
||||
// Stash the event so it can be triggered later.
|
||||
deferredPrompt = e;
|
||||
// Update UI to notify the user they can add to home screen
|
||||
addBtn.style.display = 'block';
|
||||
|
||||
addBtn.addEventListener('click', (e) => {
|
||||
// hide our user interface that shows our A2HS button
|
||||
//addBtn.style.display = 'none';
|
||||
// Show the prompt
|
||||
deferredPrompt.prompt();
|
||||
// Wait for the user to respond to the prompt
|
||||
deferredPrompt.userChoice.then((choiceResult) => {
|
||||
if (choiceResult.outcome === 'accepted') {
|
||||
console.log('User accepted the prompt, app installed');
|
||||
addBtn.style.display = 'none';
|
||||
removeBtn.style.display = 'block';
|
||||
} else {
|
||||
console.log('User dismissed the prompt');
|
||||
}
|
||||
deferredPrompt = null;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
2
ram/driver/static/wt/js/roundslider.min.js
vendored
Normal file
2
ram/driver/static/wt/js/roundslider.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
586
ram/driver/static/wt/js/storageController.js
Normal file
586
ram/driver/static/wt/js/storageController.js
Normal file
@@ -0,0 +1,586 @@
|
||||
/* This is part of the DCC++ EX Project for model railroading and more.
|
||||
For licence information, please see index.html.
|
||||
For more information, see us at dcc-ex.com.
|
||||
|
||||
storageController.js
|
||||
|
||||
Manages the setting storage capabilities
|
||||
*/
|
||||
|
||||
$(document).ready(function(){
|
||||
// This is displays message about Local storage Support of the Local browser
|
||||
if (typeof Storage !== "undefined") {
|
||||
console.log("Your browser supports Local Storage");
|
||||
} else {
|
||||
console.log("Sorry! Your browser does not supporting Local Storage");
|
||||
}
|
||||
|
||||
// Opens NEW MAP window with all fields empty
|
||||
$("#add-map").on("click", function () {
|
||||
$("#save-fn-map").attr("mode", "new");
|
||||
$(".fn-heading").html("New Mapping");
|
||||
showBtnConfig({ mname: "", fnData: fnMasterData });
|
||||
});
|
||||
|
||||
// This will Load buttons on selecting a map from the select box
|
||||
$("#select-map").change(function () {
|
||||
selectedval = $(this).val();
|
||||
if (selectedval != "default") {
|
||||
data = getStoredMapData(selectedval);
|
||||
loadButtons(data);
|
||||
} else {
|
||||
loadButtons({ mname: "default", fnData: fnMasterData });
|
||||
}
|
||||
});
|
||||
|
||||
// Opens MAP window with all fields filled from the selected map that allows editing Map
|
||||
$(document).on("click", "#edit-cur-map", function () {
|
||||
$("#save-fn-map").attr("mode", "edit");
|
||||
$(".fn-heading").html("Edit Mapping");
|
||||
selectedval = $("#cur-map-val").attr("cur-map");
|
||||
if (selectedval != "Default") {
|
||||
data = getStoredMapData(selectedval);
|
||||
showBtnConfig(data);
|
||||
} else {
|
||||
alert("Cannot edit Default mapping!");
|
||||
}
|
||||
//showBtnConfig();
|
||||
});
|
||||
|
||||
// Closes MAP window on clicking X icon
|
||||
$("#close-model").on("click", function () {
|
||||
$("#fnModal").hide();
|
||||
});
|
||||
|
||||
//This will check for Save mode (NEW MAP / EDIT MAP) and delegate the functionality
|
||||
$("#save-fn-map").on("click", function () {
|
||||
mode = $(this).attr("mode");
|
||||
// alert(mode); // debug line
|
||||
if (mode == "new") {
|
||||
addNewMap();
|
||||
} else {
|
||||
editMap();
|
||||
}
|
||||
});
|
||||
|
||||
//Allows user to download the selected map in .JSON format
|
||||
$(document).on("click", "#export-cur-map", function () {
|
||||
map = $("#cur-map-val").attr("cur-map");
|
||||
if (map != "default") {
|
||||
downloadMapData(map);
|
||||
} else {
|
||||
alert("Please select Custom map");
|
||||
}
|
||||
});
|
||||
|
||||
// This remove whole exthrottle app data but with confirmation
|
||||
$("#wipe-map").on("click", function () {
|
||||
var r = confirm("Are you sure on deletion?");
|
||||
if (r == true) {
|
||||
window.localStorage.removeItem("mapData");
|
||||
console.log("!!!!!!MAPS WIPED!!!!!!");
|
||||
loadmaps();
|
||||
}
|
||||
});
|
||||
|
||||
// This allows user to download whole exthrottle app data
|
||||
$("#export-all-maps").on("click", function () {
|
||||
exportMapData();
|
||||
});
|
||||
|
||||
// This allows user to upload previously downloaded Map (JSON format must adhere)
|
||||
$(document).on("click", "#import-map", function (e) {
|
||||
e.preventDefault();
|
||||
$("#map-upload").trigger("click");
|
||||
});
|
||||
// This part with above which is responsible for actual file upload for MAP
|
||||
$("#map-upload").on("change", function (e) {
|
||||
var file = e.target.files[0];
|
||||
var field = $(this);
|
||||
var freader = new FileReader();
|
||||
freader.onload = function (evt) {
|
||||
data = JSON.parse(evt.target.result);
|
||||
setMapData(data);
|
||||
field.val("");
|
||||
};
|
||||
freader.readAsText(file);
|
||||
});
|
||||
|
||||
// This is responsible for Cab upload
|
||||
$("#cabs-upload").on("change", function (e) {
|
||||
var file = e.target.files[0];
|
||||
var field = $(this);
|
||||
var freader = new FileReader();
|
||||
freader.onload = function (evt) {
|
||||
data = JSON.parse(evt.target.result);
|
||||
importLocoData(data);
|
||||
field.val("");
|
||||
};
|
||||
freader.readAsText(file);
|
||||
});
|
||||
|
||||
// This allows user to upload previously downloaded MAP DATA (JSON format must adhere)
|
||||
$(document).on("click", "#import-all-maps", function (e) {
|
||||
e.preventDefault();
|
||||
//$("#appdata-upload").attr("mode", "mapData");
|
||||
$("#maps-upload").trigger("click");
|
||||
});
|
||||
|
||||
// This part of above which is responsible for actual file upload for MAP DATA
|
||||
$("#maps-upload").on("change", function (e) {
|
||||
var file = e.target.files[0];
|
||||
var field = $(this);
|
||||
var freader = new FileReader();
|
||||
freader.onload = function (evt) {
|
||||
data = JSON.parse(evt.target.result);
|
||||
importMapdata(data);
|
||||
field.val("");
|
||||
};
|
||||
freader.readAsText(file);
|
||||
});
|
||||
|
||||
// Set height of throttle container according to functions panel
|
||||
$(".throttle-container").height($(".functionKeys").first().height());
|
||||
|
||||
//Temparory function Shows APP DATA in console
|
||||
$("#loco-info").on("click", function () {
|
||||
console.log(getMapData());
|
||||
});
|
||||
|
||||
//Functions for the storage page in settings
|
||||
|
||||
$("#backup-app-settings").on("click", function () {
|
||||
exportAppData();
|
||||
});
|
||||
|
||||
// This allows user to upload previously downloaded APP DATA (JSON format must adhere)
|
||||
$("#restore-app-settings").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
$("#app-upload").trigger("click");
|
||||
});
|
||||
|
||||
// This part of above which is responsible for actual file upload for APP DATA
|
||||
$("#app-upload").on("change", function (e) {
|
||||
var file = e.target.files[0];
|
||||
var field = $(this);
|
||||
var freader = new FileReader();
|
||||
freader.onload = function (evt) {
|
||||
data = JSON.parse(evt.target.result);
|
||||
importAppdata(data);
|
||||
field.val("");
|
||||
};
|
||||
freader.readAsText(file);
|
||||
});
|
||||
|
||||
$("#wipe-app-settings").on("click", function () {
|
||||
var r = confirm("Are you sure on deletion?");
|
||||
if (r == true) {
|
||||
window.localStorage.removeItem("mapData");
|
||||
window.localStorage.removeItem("cabList");
|
||||
window.localStorage.removeItem("userpref");
|
||||
console.log("!!!!!DATA IS WIPED!!!!!!");
|
||||
loadmaps();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Load all maps to select box
|
||||
function loadmaps(){
|
||||
$("#select-map").empty();
|
||||
$("#select-map").append($("<option />").val("default").text("Default"));
|
||||
getMapData().forEach(map => {
|
||||
$("#select-map").append($("<option />").val(map.mname).text(map.mname));
|
||||
})
|
||||
}
|
||||
|
||||
// Load button layout of selected Map
|
||||
function loadButtons(data){
|
||||
$("#fn-wrapper").empty();
|
||||
$.each(data.fnData, function(key, value){
|
||||
isPressed = value[0] != 0 ? true : false;
|
||||
btnType = value[1] != 0 ? "press" : "toggle";
|
||||
if(value[3]==1){
|
||||
$("#fn-wrapper").append(
|
||||
"<div class='fn-button form-group field-button-fn'> <button class='btn-default btn fn-btn "+btnType+"' data-type='"+
|
||||
btnType+"' aria-pressed='"+isPressed+"' name='"+key+"' id='"+key+"'>"+
|
||||
value[2]+"</button>"
|
||||
+"</div>");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show the Custom Map fields inside Custom map window while adding and editing a Map.
|
||||
function showBtnConfig(data){
|
||||
|
||||
$("#fnModal").show();
|
||||
$('#fnModal').css({"top":"7%", "left": "18%"});
|
||||
$('#fnModal').draggable();
|
||||
$("#fnModal .fn-modal-content").empty();
|
||||
$("#fnModal .fn-modal-content").append('<div class="row header-row"><div class="column-2 header-col func-title">Map Name</div> <div class="column-5 header-col"><input type="text" class="fn-input" id="map-name" value="'+data.mname+'"/></div> <div class="column-3 header-col"></div></div>');
|
||||
$("#fnModal .fn-modal-content").append('<div class="row header-row"><div class="column-1 header-col">Function</div> <div class="column-4 header-col">Label</div> <div class="column-3 header-col">Button Type</div><div class="column-2 header-col">Visibility</div></div>');
|
||||
$.each(data.fnData, function(key, value){
|
||||
isPressed = value[0] != 0 ? true : false;
|
||||
btnType = value[1] != 0 ? "press" : "toggle";
|
||||
btnpress = value[1] == 1 ? "checked" : "";
|
||||
btnToggle = value[1] == 0 ? "checked" : "";
|
||||
fvisible = value[3] == 1 ? "checked" : "";
|
||||
$("#fnModal .fn-modal-content").append('<div class="row edit-row" id="'+key+'">'+
|
||||
'<div class="column-1 func-title">'+key +'</div>'+
|
||||
'<div class="column-4"> <input class="fn-input" name="'+key+'" id="'+key+'" value="'+value[2]+'"/>'+
|
||||
'<span class="focus-border"><i></i></span>'+
|
||||
'</div>'+
|
||||
'<div class="fn-radio column-3" name="'+key+'Type" id="'+key+'Type">'+
|
||||
'<input type="radio" id="'+key+'press" name="btn'+key+'Type" value="press" '+btnpress+'/>'+
|
||||
'<label for="'+key+'press">Momentary</label> '+
|
||||
'<input type="radio" id="'+key+'toggle" name="btn'+key+'Type" value="toggle" '+btnToggle+'/>'+
|
||||
'<label for="'+key+'toggle">Latching</label>'+
|
||||
'</div>'+
|
||||
'<div class="fn-chkbox column-2" name="'+key+'Visible" id="'+key+'Type">'+
|
||||
'<input type="checkbox" id="'+key+'Visible" name="'+key+'Visible" '+fvisible+'/>'+
|
||||
'<label for="'+key+'Visible">Show</label> '+
|
||||
'</div>'+
|
||||
'</div>');
|
||||
});
|
||||
}
|
||||
|
||||
// Saves New Map data to Local storage
|
||||
function addNewMap(){
|
||||
customFnData = {};
|
||||
$(".edit-row").each(function (val) {
|
||||
key = $(this).find(".func-title").text();
|
||||
btnType = $(this).children().find("input[type='radio']:checked").val() == "press" ? 1 : 0;
|
||||
fnvisible = $(this).children().find("input[type='checkbox']").prop("checked") ? 1 : 0;
|
||||
arr = [0, btnType, $(this).children().find(".fn-input").val(), fnvisible];
|
||||
customFnData[key] = arr;
|
||||
});
|
||||
mapName = $.trim($("#map-name").val());
|
||||
if (!ifExists(mapName)) {
|
||||
if (mapName) {
|
||||
// Send data to store in Local storage
|
||||
setMapData({ mname: mapName, fnData: customFnData });
|
||||
$("#fnModal").hide();
|
||||
setFunctionMaps();
|
||||
alert("Map Saved Sucessfully");
|
||||
} else {
|
||||
alert("Name is missing!!");
|
||||
}
|
||||
} else {
|
||||
alert("Map with the Name already exists!! Please change the Map name..");
|
||||
}
|
||||
}
|
||||
|
||||
// Saves Edited Map data to local storage
|
||||
function editMap(){
|
||||
customFnData = {};
|
||||
$(".edit-row").each(function(val){
|
||||
key = $(this).find(".func-title").text();
|
||||
btnType = $(this).children().find("input[type='radio']:checked").val() == "press" ? 1 : 0;
|
||||
fnvisible = $(this).children().find("input[type='checkbox']").prop('checked') ? 1 : 0;
|
||||
arr = [ 0, btnType, $(this).children().find(".fn-input").val(), fnvisible ];
|
||||
customFnData[key] = arr;
|
||||
});
|
||||
mapName = $.trim($("#map-name").val());
|
||||
if (mapName) {
|
||||
setMapData({ mname: mapName, fnData: customFnData });
|
||||
$("#fnModal").hide();
|
||||
setFunctionMaps();
|
||||
loadMapData(mapName);
|
||||
alert("Map Saved Sucessfully");
|
||||
} else {
|
||||
alert("Name is missing!!");
|
||||
}
|
||||
}
|
||||
|
||||
//*** Saves given data into Local storage**/
|
||||
// Create new data object if one does not exist
|
||||
// Verify if the given Map data already exists and replace it
|
||||
// Or Create new map data and inserts it into local storage object
|
||||
// Finally Saves the Data into Local storage */
|
||||
function setMapData(mapdata) {
|
||||
if (typeof Storage === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
const smapdata = getMapData()
|
||||
|
||||
if (ifExists(mapdata.mname)) {
|
||||
smapdata.find(function (item, i) {
|
||||
if (item.mname == mapdata.mname) {
|
||||
item.fnData = mapdata.fnData;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
smapdata.push(mapdata);
|
||||
}
|
||||
window.localStorage.setItem('mapData', JSON.stringify(smapdata));
|
||||
|
||||
loadmaps();
|
||||
setFunctionMaps();
|
||||
loadMapData(mapdata.mname);
|
||||
$("#select-map").val(mapdata.mname).trigger("change");
|
||||
|
||||
}
|
||||
|
||||
//Returns the Map data of given Map name
|
||||
function getStoredMapData(name){
|
||||
const data = getMapData();
|
||||
if(data !=null){
|
||||
return data.find(function(item, i){
|
||||
if(item.mname == name){
|
||||
return item.fnData;
|
||||
}
|
||||
});
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//Download the Map data of given Map name
|
||||
function downloadMapData(mapName){
|
||||
data = JSON.stringify(getStoredMapData(mapName));
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = mapName+".json";
|
||||
a.click();
|
||||
}
|
||||
|
||||
//Delete the Map data of given Map name
|
||||
function deleteFuncData(name){
|
||||
var r = confirm("Are you sure on deletion?");
|
||||
if (r == true) {
|
||||
if (typeof(Storage) !== "undefined") {
|
||||
curmapdata = [];
|
||||
const data = getMapData()
|
||||
if(!data){
|
||||
alert("No Data stored");
|
||||
}else{
|
||||
data.find(function(item, i){
|
||||
if(item.mname != name){
|
||||
curmapdata.push(item);
|
||||
}
|
||||
});
|
||||
window.localStorage.setItem('mapData', JSON.stringify(curmapdata));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Map data of ExWebThrottle
|
||||
* @return {[]}
|
||||
*/
|
||||
function getMapData(){
|
||||
if (typeof Storage === "undefined") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const localMapData = JSON.parse(window.localStorage.getItem("mapData"));
|
||||
|
||||
return localMapData || []
|
||||
}
|
||||
|
||||
// Returns boolen if the given Map exists in local storage
|
||||
function ifExists(name) {
|
||||
const data = getMapData()
|
||||
const existingItem = data.find((item) => item.mname === name);
|
||||
return !!existingItem;
|
||||
}
|
||||
|
||||
//Download all Maps of EXthrottle
|
||||
function exportMapData() {
|
||||
const data = getMapData()
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = "ListofMaps.json";
|
||||
a.click();
|
||||
}
|
||||
|
||||
function importMapdata(data){
|
||||
if(data){
|
||||
window.localStorage.setItem('mapData', JSON.stringify(data));
|
||||
loadmaps();
|
||||
$("#select-map").val('default').trigger("change");
|
||||
setFunctionMaps();
|
||||
}
|
||||
}
|
||||
//Import the Locomotives List data
|
||||
function importLocoData(data) {
|
||||
if (data) {
|
||||
window.localStorage.setItem("cabList", JSON.stringify(data));
|
||||
loadLocomotives();
|
||||
locoList = getLocoList();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************/
|
||||
/********** Locomotives Data functions ***********/
|
||||
/*************************************************/
|
||||
|
||||
function saveLocomotive(data){
|
||||
locodata = $(data).arrayToJSON();
|
||||
if (typeof Storage !== "undefined") {
|
||||
curCabList = [];
|
||||
cabData = JSON.parse(window.localStorage.getItem("cabList"));
|
||||
if (!cabData) {
|
||||
curCabList.push(locodata);
|
||||
window.localStorage.setItem("cabList", JSON.stringify(curCabList));
|
||||
return true;
|
||||
} else {
|
||||
cabData.push(locodata);
|
||||
window.localStorage.setItem("cabList", JSON.stringify(cabData));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function saveEditedLocomotive(data, id){
|
||||
locodata = $(data).arrayToJSON();
|
||||
if (typeof Storage !== "undefined") {
|
||||
cabData = JSON.parse(window.localStorage.getItem("cabList"));
|
||||
cabData.find(function (item, i) {
|
||||
if (i == id) {
|
||||
cabData[i] = locodata;
|
||||
}
|
||||
});
|
||||
window.localStorage.setItem("cabList", JSON.stringify(cabData));
|
||||
}
|
||||
}
|
||||
|
||||
function ifLocoExists(name) {
|
||||
data = JSON.parse(window.localStorage.getItem("cabList"));
|
||||
found = false;
|
||||
if (data != null) {
|
||||
data.find(function (item, i) {
|
||||
if (item.name == name) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
// Returns the AppData of ExWebThrottle
|
||||
function getLocoList(){
|
||||
if (typeof Storage !== "undefined") {
|
||||
return JSON.parse(window.localStorage.getItem("cabList"));
|
||||
}else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
//Download the Locomotives List data
|
||||
function downloadCabData(){
|
||||
data = JSON.stringify(getLocoList());
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = "CabList.json";
|
||||
a.click();
|
||||
}
|
||||
|
||||
// Returns the LocoData of ExWebThrottle
|
||||
function getStoredLocoData(name) {
|
||||
console.log(name);
|
||||
data = JSON.parse(window.localStorage.getItem("cabList"));
|
||||
if (data != null) {
|
||||
return data.find(function (item, i) {
|
||||
if (item.name == name) {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************/
|
||||
/************** Preferences ***************/
|
||||
/********************************************/
|
||||
|
||||
// Get a given user preference
|
||||
function getPreference(pref){
|
||||
if (window.localStorage.getItem("userpref") != null) {
|
||||
curpref = JSON.parse(window.localStorage.getItem("userpref"));
|
||||
return curpref[pref];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Set a given user preference
|
||||
function setPreference(pref, val){
|
||||
if (window.localStorage.getItem("userpref") != null){
|
||||
curpref = JSON.parse(window.localStorage.getItem("userpref"));
|
||||
}else{
|
||||
curpref = {};
|
||||
}
|
||||
curpref[pref] = val;
|
||||
setUserPreferences(curpref);
|
||||
}
|
||||
|
||||
// Store user preferences in local storage
|
||||
function setUserPreferences(pref){
|
||||
if (typeof(Storage) !== "undefined") {
|
||||
window.localStorage.setItem("userpref", JSON.stringify(pref));
|
||||
}
|
||||
}
|
||||
|
||||
function getUserPreferences() {
|
||||
if (typeof Storage !== "undefined") {
|
||||
return JSON.parse(window.localStorage.getItem("userpref"));
|
||||
}else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function importPrefData(data) {
|
||||
if (data) {
|
||||
window.localStorage.setItem("userpref", JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
function exportAppData(){
|
||||
const jsonObj = [
|
||||
{maps: getMapData()},
|
||||
{locos: getLocoList()},
|
||||
{preferences: getUserPreferences()}
|
||||
]
|
||||
const data = JSON.stringify(jsonObj);
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([data], { type: "application/json" });
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = "AppData.json";
|
||||
a.click();
|
||||
|
||||
}
|
||||
|
||||
function importAppdata(data){
|
||||
importMapdata(data[0]["maps"]);
|
||||
importLocoData(data[1]["locos"]);
|
||||
importPrefData(data[2]["preferences"]);
|
||||
setThrottleScreenUI();
|
||||
}
|
||||
|
||||
|
||||
(function ($) {
|
||||
$.fn.arrayToJSON = function () {
|
||||
var o = {};
|
||||
$.each($(this), function () {
|
||||
if (o[this.name]) {
|
||||
if (!o[this.name].push) {
|
||||
o[this.name] = [o[this.name]];
|
||||
}
|
||||
o[this.name].push(this.value || "");
|
||||
} else {
|
||||
o[this.name] = this.value || "";
|
||||
}
|
||||
});
|
||||
return o;
|
||||
};
|
||||
}
|
||||
)(jQuery);
|
Reference in New Issue
Block a user