/********************************************************************************************** * * Javascript for 'webThrottle.html' * * This script defines the web throttle behaviour. * * >>> This file version: 2.4 - by Oscar Moutinho (oscar.moutinho@gmail.com) * * This script relies on 'jquery.jmriConnect.js v2.1' (read its header for dependencies). * * URL parameters: (roster/panels list if no parameters) * - 'loconame' (to open a throttle for a loco) * - 'turnouts' (to list turnouts) * - 'routes' (to list routes) * - 'panelname' (to open a panel) * - 'reset' (to remove local configuration - restore defaults) * - 'debug' (=true -> turn it ON, =false -> turn it OFF, other values -> nothing change) * **********************************************************************************************/ //+++++++++++++++++++++++++++++++++++++++++++++++++++ Global Vars and Functions //----------------------------------------- Global vars var log = new Logger(); var $debug = true; var $vScrollbarWidth; var $showScrollBar = false; var $zIndexForSmoothAlert = 999999999; // Max z-index = 2147483647 var $jmri = null; var $throttleType = ''; var $rosterGroup = ''; var $isRoster; var $frameList = []; var $toFrame = false; var $inFrame = false; var $pageInFrame; var $speedPosition = true; var $inputParameters; var $paramLocoName; var $paramPanels; var $paramPanelName; var $rosterGroups; var $locoList; var $panelLoaded = false; var $resizeCheckTimer = null; var $resizeCheckInterval = 500; // ms var $viewportHeight = 0; // Initial value to force resize var $viewportWidth = 0; // Initial value to force resize var $changeBothSizes; var $heightChanged; var $widthChanged; var $portrait; var $fontSizeMin = 8; // px var $fontSizeMax = 48; // px var $defaultFontSize = 16; // px - default var $fontSize = $defaultFontSize; var $fontDelta; var $fontChanged; var $sizeCtrlPercent; var $nextBlockTop; var $headerHeightRef = 40; // px var $buttonsHeightRef = 30; // px var $cellHeightRef = 100; // px var $cellWidthRef = 500; // px var $speedWidthRef = 80; // px var $functionHeightRef = 80; // px var $functionWidthRef = 250; // px var $buttonDelayTimeout = 1000; // ms var $powerDelayTimer = null; var $removeDelayTimer = null; var $speedTimer = null; var $speedInterval = 250; // ms var $speedStep = 0.10; var $speedFeedback = true; var $speedAux = 0; var $hasTouch = ('ontouchstart' in window); var $hasMovement = (window.orientation !== null) && $hasTouch; var $orientation = null; var $movementTilt = null; var $movementActive = false; var $movementOn = false; var $movementCtrl = 0; var $locoAddress = "none"; var $help = []; var $useCamera = true; // set to true to enable camera view var $locoVideoURL = null; //----------------------------------------- Generic onError window.onerror = function(errMsg, errUrl, errLineNumber) { if ($jmri) $jmri.closeSocket(); if ($resizeCheckTimer) { clearInterval($resizeCheckTimer); $resizeCheckTimer = null; } if ($speedTimer) { clearInterval($speedTimer); $speedTimer = null; } if (errMsg.indexOf('private~') >= 0) alert(errMsg.split('~')[1]); else { if (errMsg == 'Uncaught ReferenceError: stopme is not defined') location.reload(true); // I don't know what this is !?!?!? Just reload. else alert('\nError running javascript:\n' + errMsg + '\n\nURL:\n' + errUrl + '\n\nLine Number: ' + errLineNumber); } document.body.innerHTML = ''; return true; }; //----------------------------------------- Page exit cleanup 1 window.onbeforeunload = function() { }; //----------------------------------------- Page exit cleanup 2 window.onunload = function() { if ($jmri) { if ($('body').attr('locoReady') == 'true') $jmri.setJMRI('throttle', $locoAddress, {"release":null}); $jmri.closeSocket(); } if ($resizeCheckTimer) { clearInterval($resizeCheckTimer); $resizeCheckTimer = null; } if ($speedTimer) { clearInterval($speedTimer); $speedTimer = null; } }; //----------------------------------------- Add trim method to string String.prototype.trim = function () {return this.replace(/^\s+|\s+$/g,'');}; //----------------------------------------- Immediate execution try { if (jQuery === undefined) throw new Error('private~jQuery not loaded.\nHTML5 and WebSockets needed.\nCheck browser compatibility.'); } catch(error) { throw new Error('private~jQuery not loaded.\nHTML5 and WebSockets needed.\nCheck browser compatibility.'); } try { localStorage['webThrottle.test'] = '1'; localStorage.removeItem('webThrottle.test'); } catch(error) { if (error.code === DOMException.QUOTA_EXCEEDED_ERR && localStorage.length === 0) throw new Error('private~Turn off Private Browsing.'); else throw new Error('private~Local Storage not available.\nHTML5 and WebSockets needed.\nCheck browser compatibility.'); } //----------------------------------------- Run at start up $(document).ready(function() { /******* Constants available in '$jmri' object * $jmri.powerUNKNOWN = 0; * $jmri.powerON = 2; * $jmri.powerOFF = 4; * $jmri.turnoutUNDEFINED = 0; * $jmri.turnoutUNKNOWN = 1; * $jmri.turnoutCLOSED = 2; * $jmri.turnoutTHROWN = 4; * $jmri.routeDISABLED = 0; * $jmri.routeUNDEFINED = 1; * $jmri.routeACTIVE = 2; * $jmri.routeINACTIVE = 4; * $jmri.TRUE = true; * $jmri.FALSE = false; * $jmri.YES = 1; * $jmri.NO = 0; * $jmri.EMERGENCY_STOP = '-1.0'; * $jmri.STOP = '0.0'; * $jmri.FULL_SPEED = '1.0'; ******** Functions available in '$jmri' object * $jmri.getRoster(group) * . To list all: group = null or undefined * . Returns array of object: id, roadNumber, roadName, mfg, owner, model, dccAddress, imageFilePath, iconFilePath * $jmri.getRosterItem(id) * . Returns object: id, fileName, roadNumber, roadName, mfg, owner, model, dccAddress, comment, maxSpeed, imageFilePath, iconFilePath, URL, IsShuntingOn, f[i].lockable, f[i].functionlabel, f[i].functionImage, f[i].functionImageSelected * . If a function is not defined: f[i] = null * $jmri.getRosterGroups() * . Returns array of strings: rosterGroup * $jmri.getObjectList(listType) { * . Possible values for string 'listType' (get): roster, panels, lights, reporters, sensors, turnouts, signalHeads, signalMasts, routes, memories, blocks, oblocks * . Returns array of objects: list * $jmri.closeSocket() * . To stop communication with JMRI (usually, before exit and before blocking code: alert(), ...) * $jmri.getJMRI(type, name) * . Possible values for string 'type' (get): light, reporter, sensor, turnout, signalHead, signalMast, route, memory, power, rosterEntry * $jmri.setJMRI(type, name, args) * . Possible values for string 'type' (set): light, reporter, sensor, turnout, signalHead, signalMast, route, memory, power * Special case for 'type' = 'power' > string 'name' should be null * Possible 'args' for 'throttle': {"throttle":throttleName,"address":dccAddress,"speed":speed,"forward":forward,"Fn":active} (0 <= n <= 28) * Possible 'args' for 'light': {"userName":userName,"comment":comment,"state":state} * Possible 'args' for 'reporter': {"userName":userName,"state":state,"comment":comment,"report":report,"lastReport":lastReport} * Possible 'args' for 'sensor': {"userName":userName,"comment":comment,"inverted":inverted,"state":state} * Possible 'args' for 'turnout': {"userName":userName,"comment":comment,"inverted":inverted,"state":state} * Possible 'args' for 'signalHead': {"userName":userName,"comment":comment,"lit":lit,"appearance":appearance,"held":held,"state":state,"appearanceName":appearanceName} * Possible 'args' for 'signalMast': {"userName":userName,"":aspect,"lit":lit,"held":held,"state":state} * Possible 'args' for 'route': {"userName":userName,"comment":comment,"state":state} * Possible 'args' for 'memory': {"userName":userName,"comment":comment,"value":value} * Possible 'args' for 'power': {"state":state} * block * oblock * >>> Other values for 'type' and new 'args' may be available ********************************************/ var debug = loadLocalInfo('webThrottle.debug'); if (debug == 'true' || debug == 'false') $debug = (debug == 'true'); else saveLocalInfo('webThrottle.debug', $debug = false); $fontDelta = 0.14; // hardcoded from testing var fS = loadLocalInfo('webThrottle.fontSize'); if (fS && !isNaN(fS) && Number(fS) >= $fontSizeMin && Number(fS) <= $fontSizeMax) $fontSize = Number(fS); else saveLocalInfo('webThrottle.fontSize', $fontSize); $vScrollbarWidth = getVerticalScrollbarWidth(); loadFrameList(); for (var i = 0; i < $frameList.length; i++) { // Check if this page is flagged to be displayed in a frame if ($frameList[i] == document.URL) { $inFrame = true; break; } } var speedPosition = loadLocalInfo('webThrottle.speedPosition'); if (speedPosition == 'true' || speedPosition == 'false') $speedPosition = (speedPosition == 'true'); else saveLocalInfo('webThrottle.speedPosition', $speedPosition); var movementActive = loadLocalInfo('webThrottle.movementActive'); if (movementActive == 'true' || movementActive == 'false') $movementActive = (movementActive == 'true'); else saveLocalInfo('webThrottle.movementActive', $movementActive); $inputParameters = getUrlParameters(); debug = $inputParameters.debug; if (debug) debug = debug.toLowerCase(); if (debug == 'true' || debug == 'false') { $debug = (debug == 'true'); saveLocalInfo('webThrottle.debug', $debug); alert('Debug mode ' + ($debug ? 'started.' : 'stopped.')); window.open(document.URL.split('?')[0], '_top'); // Stop building throttle and restart without parameters return; } if ($inputParameters.reset !== undefined) { // Remove all application parameters removeLocalInfo("webThrottle.debug"); removeLocalInfo("webThrottle.fontSize"); removeLocalInfo("webThrottle.speedPosition"); removeLocalInfo("webThrottle.movementActive"); removeLocalInfo("webThrottle.rosterGroup"); removeLocalInfo("webThrottle.frameListSize"); for(var i = 0; i < $frameList.length; i++) removeLocalInfo("webThrottle.frameList[" + i + "]"); alert('Default configuration restored.'); window.open(document.URL.split('?')[0], '_top'); // Stop building throttle and restart without parameters return; } if ($debug) smoothAlert('In debug mode ...', 3); $paramLocoName = $inputParameters.loconame; $paramPanelName = $inputParameters.panelname; if ($paramLocoName) $throttleType = 'loco'; if (!$throttleType) if ($inputParameters.turnouts !== undefined) $throttleType = 'turnouts'; if (!$throttleType) if ($inputParameters.routes !== undefined) $throttleType = 'routes'; if (!$throttleType) if ($paramPanelName) $throttleType = 'panel'; if (!$throttleType) $throttleType = 'roster'; $pageInFrame = (window.parent.$('iframe').length > 0); if ($frameList.length && $throttleType == 'roster' && !$pageInFrame) { // Has info for frames, is the master and it is the top window (build frameset) var body = $('body').attr('id', 'bodyFrames'); var frame = $('