mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 16:47:59 +08:00
ggwave-wasm : add html shell
This commit is contained in:
@@ -16,3 +16,8 @@ target_link_libraries(${TARGET} PRIVATE
|
||||
ggwave-common
|
||||
ggwave-common-sdl2
|
||||
)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/style.css COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/main.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/main.js COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plucky.mp3 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/plucky.mp3 COPYONLY)
|
||||
|
||||
400
examples/ggwave-wasm/index-tmpl.html
Normal file
400
examples/ggwave-wasm/index-tmpl.html
Normal file
@@ -0,0 +1,400 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ggwave : emscripten example</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
|
||||
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<h1>ggwave</h1>
|
||||
|
||||
Open this page on multiple devices (computers, phones, tables, etc.). <br>
|
||||
Press the init button and broadcast some text. Make sure your speakers and microphones are enabled.
|
||||
|
||||
<br><br>
|
||||
|
||||
<section>
|
||||
<div id="sound"></div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan=3 align="center">
|
||||
<button onClick="doInit()" id="butInit" disabled>Init</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=3 align="center">
|
||||
<div class="led-box">
|
||||
<p>Output:</p>
|
||||
</div>
|
||||
<div class="led-box">
|
||||
<p>Capture:</p>
|
||||
</div>
|
||||
<div class="led-box">
|
||||
<p>Browser:</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=3 align="center">
|
||||
<div class="led-box" id="has-device-output" title="Indicates if an audio output device is available">
|
||||
<div class="led-yellow"></div>
|
||||
</div>
|
||||
<div class="led-box" id="has-device-capture" title="Indicates if an audio capture device is available. Make sure this page has access to the microphone">
|
||||
<div class="led-yellow"></div>
|
||||
</div>
|
||||
<div class="led-box" id="is-browser-supported" title="Indicates if the browser is supported">
|
||||
<div class="led-yellow"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="main-controls" hidden>
|
||||
<tr>
|
||||
<td colspan=1>
|
||||
Tx protocol:
|
||||
<select id="waveConfig" onChange="selectConfig(this.value);" title="Before broadcast/receive make sure this is set to the same value for all peers">
|
||||
<option value=0>Normal</option>
|
||||
<option value=1 selected>Fast</option>
|
||||
<option value=2>Fastest</option>
|
||||
<option value=3>Ultrasonic</option>
|
||||
</select>
|
||||
<label hidden id="paramFreqDelta"></label>
|
||||
<label hidden id="paramFreqStart"></label>
|
||||
<label hidden id="paramFramesPerTx"></label>
|
||||
<label hidden id="paramBytesPerTx"></label>
|
||||
</td>
|
||||
<td colspan=1>
|
||||
Volume:
|
||||
<input type="range" min="1" max="100" value="10" class="slider" id="paramVolume" onInput="updateScroll('Volume');">
|
||||
/ <label id="paramVolumeScroll"></label></td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=1>
|
||||
<div>Rx data:</div> <textarea name="textarea" id="rxData" style="width:300px;height:100px;" disabled>
|
||||
</textarea>
|
||||
</td>
|
||||
<td colspan=1>
|
||||
<div>Tx Data:</div> <textarea name="textarea" id="txData" style="width:300px;height:100px;">This is the message to transmit.</textarea><br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=1>
|
||||
<a id="peer-info"></a>
|
||||
</td>
|
||||
<td colspan=1>
|
||||
<button onClick="lockoutSubmit(this); transmitText(document.getElementById('txData').value);">Broadcast</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<span id="status"></span>
|
||||
</section>
|
||||
|
||||
<br><hr>
|
||||
|
||||
<p>Standard output:</p>
|
||||
<textarea id="output" rows="8"></textarea>
|
||||
|
||||
<div class="spinner" id='spinnerEm'></div>
|
||||
<div class="emscripten" id="statusEm">Downloading...</div>
|
||||
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="progressEm" hidden=1></progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cell-version">
|
||||
<span>
|
||||
|
|
||||
Build time: <span class="nav-link">@GIT_DATE@</span> |
|
||||
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/ggwave/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
|
||||
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
|
||||
</span>
|
||||
</div>
|
||||
<div class="cell-about">
|
||||
<a class="nav-link" href="https://github.com/ggerganov/ggwave/tree/master"><span class="d-none d-sm-inline">View on GitHub </span>
|
||||
<svg version="1.1" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-mark-github" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var bufferRx = null;
|
||||
var brx = new Uint8Array(256);
|
||||
|
||||
function lockoutSubmit(button) {
|
||||
var oldValue = button.value;
|
||||
|
||||
button.setAttribute('disabled', true);
|
||||
button.value = '...processing...';
|
||||
|
||||
setTimeout(function(){
|
||||
button.value = oldValue;
|
||||
button.removeAttribute('disabled');
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
function selectConfig(configId) {
|
||||
paramFreqDelta.value = 1;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 9;
|
||||
paramBytesPerTx.value = 3;
|
||||
|
||||
if (configId == 1) {
|
||||
paramFreqDelta.value = 1;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 6;
|
||||
paramBytesPerTx.value = 3;
|
||||
}
|
||||
|
||||
if (configId == 2) {
|
||||
paramFreqDelta.value = 1;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 3;
|
||||
paramBytesPerTx.value = 3;
|
||||
}
|
||||
|
||||
if (configId == 3) {
|
||||
paramFreqDelta.value = 1;
|
||||
paramFreqStart.value = 320;
|
||||
paramFramesPerTx.value = 9;
|
||||
paramBytesPerTx.value = 3;
|
||||
}
|
||||
|
||||
if (configId == 4) {
|
||||
paramFreqDelta.value = 4;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 9;
|
||||
paramBytesPerTx.value = 3;
|
||||
}
|
||||
|
||||
if (configId == 5) {
|
||||
paramFreqDelta.value = 2;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 18;
|
||||
paramBytesPerTx.value = 6;
|
||||
}
|
||||
|
||||
if (configId == 6) {
|
||||
paramFreqDelta.value = 2;
|
||||
paramFreqStart.value = 40;
|
||||
paramFramesPerTx.value = 9;
|
||||
paramBytesPerTx.value = 6;
|
||||
}
|
||||
|
||||
if (configId == 7) {
|
||||
paramFreqDelta.value = 4;
|
||||
paramFreqStart.value = 320;
|
||||
paramFramesPerTx.value = 9;
|
||||
paramBytesPerTx.value = 3;
|
||||
}
|
||||
|
||||
Module._setTxMode(1); // Variable length mode
|
||||
Module._setParameters(
|
||||
paramFreqDelta.value,
|
||||
paramFreqStart.value,
|
||||
paramFramesPerTx.value,
|
||||
paramBytesPerTx.value,
|
||||
0,
|
||||
paramVolume.value);
|
||||
}
|
||||
|
||||
function updateScroll(sName) {
|
||||
val = document.getElementById('param'+sName).value;
|
||||
document.getElementById('param'+sName+'Scroll').innerHTML = val;
|
||||
|
||||
Module._setParameters(
|
||||
paramFreqDelta.value,
|
||||
paramFreqStart.value,
|
||||
paramFramesPerTx.value,
|
||||
paramBytesPerTx.value,
|
||||
0,
|
||||
paramVolume.value);
|
||||
}
|
||||
|
||||
function getSampleRate() {
|
||||
if (typeof Module === 'undefined') return;
|
||||
var sampleRate = Module._getSampleRate();
|
||||
}
|
||||
|
||||
var isiOS = /iPad|iPhone|iPod|CriOS/.test(navigator.userAgent) && !window.MSStream;
|
||||
var isInitialized = false;
|
||||
var isAudioContextUnlocked = !isiOS;
|
||||
|
||||
var htmlGreenLED = "<div class=\"led-green\"></div>";
|
||||
var htmlRedLED = "<div class=\"led-red\"></div>";
|
||||
var htmlYellowLED = "<div class=\"led-yellow\"></div>";
|
||||
|
||||
function checkStatus() {
|
||||
var hasDeviceOutput = Module._hasDeviceOutput();
|
||||
{
|
||||
var el = document.getElementById('has-device-output');
|
||||
if (hasDeviceOutput != 0 && isAudioContextUnlocked) {
|
||||
if (el.innerHTML != htmlGreenLED) {
|
||||
el.innerHTML = htmlGreenLED;
|
||||
}
|
||||
} else {
|
||||
if (el.innerHTML != htmlRedLED) {
|
||||
el.innerHTML = htmlRedLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hasDeviceCapture = Module._hasDeviceCapture();
|
||||
{
|
||||
var el = document.getElementById('has-device-capture');
|
||||
if (hasDeviceCapture != 0) {
|
||||
if (el.innerHTML != htmlGreenLED) {
|
||||
el.innerHTML = htmlGreenLED;
|
||||
}
|
||||
} else {
|
||||
if (el.innerHTML != htmlRedLED) {
|
||||
el.innerHTML = htmlRedLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isBrowserSupported = !isiOS;
|
||||
{
|
||||
var el = document.getElementById('is-browser-supported');
|
||||
if (isBrowserSupported) {
|
||||
if (el.innerHTML != htmlGreenLED) {
|
||||
el.innerHTML = htmlGreenLED;
|
||||
}
|
||||
} else {
|
||||
if (el.innerHTML != htmlYellowLED) {
|
||||
el.innerHTML = htmlYellowLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var statusElement = document.getElementById('statusEm');
|
||||
var progressElement = document.getElementById('progressEm');
|
||||
var spinnerElement = document.getElementById('spinnerEm');
|
||||
|
||||
var Module = {
|
||||
doNotCaptureKeyboard: true,
|
||||
pre: [],
|
||||
preRun: [(function() {
|
||||
let constraints = {
|
||||
audio: {
|
||||
echoCancellation: false,
|
||||
autoGainControl: false,
|
||||
noiseSuppression: false
|
||||
}
|
||||
};
|
||||
|
||||
let mediaInput = navigator.mediaDevices.getUserMedia( constraints );
|
||||
}) ],
|
||||
postRun: [ (function() { document.getElementById("butInit").disabled = false; }) ],
|
||||
print: (function() {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.alue = ''; // clear browser cache
|
||||
return function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
printErr: function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
setStatus: function(text) {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = false;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) spinnerElement.style.display = 'none';
|
||||
}
|
||||
statusElement.innerHTML = text;
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
|
||||
function doInit() {
|
||||
if (isInitialized == false) {
|
||||
Module._doInit();
|
||||
|
||||
e = document.getElementById('waveConfig');
|
||||
selectConfig(e.options[e.selectedIndex].value);
|
||||
|
||||
updateScroll('Volume');
|
||||
bufferRx = Module._malloc(256);
|
||||
setInterval(updatePeerInfo, 100);
|
||||
setInterval(updateRx, 1000);
|
||||
setInterval(checkStatus, 1000);
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
playSound("plucky");
|
||||
var x = document.getElementById("main-controls");
|
||||
//x.style.display = "block";
|
||||
x.hidden = false;
|
||||
}
|
||||
|
||||
Module.setStatus('Initializing...');
|
||||
window.onerror = function(event) {
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
spinnerElement.style.display = 'none';
|
||||
Module.setStatus = function(text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
|
||||
window.addEventListener('touchstart', function() {
|
||||
if (isAudioContextUnlocked == false && SDL2.audioContext) {
|
||||
var buffer = SDL2.audioContext.createBuffer(1, 1, 22050);
|
||||
var source = SDL2.audioContext.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.connect(SDL2.audioContext.destination);
|
||||
source.start();
|
||||
|
||||
setTimeout(function() {
|
||||
if((source.playbackState === source.PLAYING_STATE || source.playbackState === source.FINISHED_STATE)) {
|
||||
isAudioContextUnlocked = true;
|
||||
Module.setStatus('Wab Audio API unlocked successfully!');
|
||||
} else {
|
||||
Module.setStatus('Failed to unlock Web Audio APIi. This browser seems to not be supported');
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}, false);
|
||||
|
||||
function playSound(filename){
|
||||
document.getElementById("sound").innerHTML='<audio id="soundInner"><source src="' + filename + '.mp3" type="audio/mpeg" /><embed hidden="true" autostart="true" loop="false" src="' + filename +'.mp3" /></audio>';
|
||||
document.getElementById("soundInner").volume = paramVolume.value/100.0;
|
||||
document.getElementById("soundInner").play();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script async type="text/javascript" src="@TARGET@.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
60
examples/ggwave-wasm/main.js
Normal file
60
examples/ggwave-wasm/main.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/*! \file main.js
|
||||
* \brief Text transfer over sound
|
||||
* \author Georgi Gerganov
|
||||
*/
|
||||
|
||||
function transmitText(sText) {
|
||||
var r = new Uint8Array(256);
|
||||
for (var i = 0; i < sText.length; ++i) {
|
||||
r[i] = sText.charCodeAt(i);
|
||||
}
|
||||
|
||||
var buffer = Module._malloc(256);
|
||||
Module.writeArrayToMemory(r, buffer, 256);
|
||||
Module._setText(sText.length, buffer);
|
||||
Module._free(buffer);
|
||||
}
|
||||
|
||||
var firstTimeFail = false;
|
||||
var peerInfo = document.querySelector('a#peer-info');
|
||||
|
||||
function updatePeerInfo() {
|
||||
if (typeof Module === 'undefined') return;
|
||||
var framesLeftToRecord = Module._getFramesLeftToRecord();
|
||||
var framesToRecord = Module._getFramesToRecord();
|
||||
var framesLeftToAnalyze = Module._getFramesLeftToAnalyze();
|
||||
var framesToAnalyze = Module._getFramesToAnalyze();
|
||||
|
||||
if (framesToAnalyze > 0) {
|
||||
peerInfo.innerHTML=
|
||||
"Analyzing Rx data: <progress value=" + (framesToAnalyze - framesLeftToAnalyze) +
|
||||
" max=" + (framesToRecord) + "></progress>";
|
||||
peerReceive.innerHTML= "";
|
||||
} else if (framesLeftToRecord > Math.max(0, 0.05*framesToRecord)) {
|
||||
firstTimeFail = true;
|
||||
peerInfo.innerHTML=
|
||||
"Transmission in progress: <progress value=" + (framesToRecord - framesLeftToRecord) +
|
||||
" max=" + (framesToRecord) + "></progress>";
|
||||
} else if (framesToRecord > 0) {
|
||||
peerInfo.innerHTML= "Analyzing Rx data ...";
|
||||
} else if (framesToRecord == 0) {
|
||||
peerInfo.innerHTML= "<p>Listening for waves ...</p>";
|
||||
} else if (framesToRecord == -1) {
|
||||
if (firstTimeFail) {
|
||||
playSound("/media/case-closed");
|
||||
firstTimeFail = false;
|
||||
}
|
||||
peerInfo.innerHTML= "<p style=\"color:red\">Failed to decode Rx data</p>";
|
||||
}
|
||||
}
|
||||
|
||||
function updateRx() {
|
||||
if (typeof Module === 'undefined') return;
|
||||
Module._getText(bufferRx);
|
||||
var result = "";
|
||||
for (var i = 0; i < 140; ++i){
|
||||
result += (String.fromCharCode((Module.HEAPU8)[bufferRx + i]));
|
||||
brx[i] = (Module.HEAPU8)[bufferRx + i];
|
||||
}
|
||||
document.getElementById('rxData').innerHTML = result;
|
||||
}
|
||||
BIN
examples/ggwave-wasm/plucky.mp3
Normal file
BIN
examples/ggwave-wasm/plucky.mp3
Normal file
Binary file not shown.
279
examples/ggwave-wasm/style.css
Normal file
279
examples/ggwave-wasm/style.css
Normal file
@@ -0,0 +1,279 @@
|
||||
body {
|
||||
margin: 0; background-color: white;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
#screen {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
font: sans-serif;
|
||||
}
|
||||
.no-sel {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
-ms-user-select:none;
|
||||
user-select:none;
|
||||
-o-user-select:none;
|
||||
}
|
||||
.cell {
|
||||
pointer-events: none;
|
||||
}
|
||||
.cell-version {
|
||||
padding-left: 4px;
|
||||
padding-top: 0.5em;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
.cell-about {
|
||||
padding-right: 24px;
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: rgba(0, 0, 0, 1.0);
|
||||
}
|
||||
|
||||
#main-container {
|
||||
font-size:12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size:12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||
div.emscripten { text-align: center; }
|
||||
div.emscripten_border { border: 1px solid black; }
|
||||
|
||||
canvas.emscripten { border: 0px none; background-color: black; }
|
||||
|
||||
.spinner {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
margin-left: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
-webkit-animation: rotation .8s linear infinite;
|
||||
-moz-animation: rotation .8s linear infinite;
|
||||
-o-animation: rotation .8s linear infinite;
|
||||
animation: rotation 0.8s linear infinite;
|
||||
|
||||
border-left: 5px solid rgb(235, 235, 235);
|
||||
border-right: 5px solid rgb(235, 235, 235);
|
||||
border-bottom: 5px solid rgb(235, 235, 235);
|
||||
border-top: 5px solid rgb(120, 120, 120);
|
||||
|
||||
border-radius: 100%;
|
||||
background-color: rgb(189, 215, 46);
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(360deg);}
|
||||
}
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(360deg);}
|
||||
}
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(360deg);}
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
#status {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 30px;
|
||||
margin-left: 20px;
|
||||
font-weight: bold;
|
||||
color: rgb(120, 120, 120);
|
||||
}
|
||||
|
||||
#progress {
|
||||
height: 20px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#output {
|
||||
width: 800px;
|
||||
height: 200px;
|
||||
margin: 0 auto;
|
||||
margin-top: 10px;
|
||||
border-left: 0px;
|
||||
border-right: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-size:10px;
|
||||
font-family: 'Lucida Console', Monaco, monospace;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.led-box {
|
||||
height: 30px;
|
||||
width: 25%;
|
||||
margin: 10px 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.led-box p {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.led-red {
|
||||
margin: 0 auto;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #F00;
|
||||
border-radius: 50%;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 12px;
|
||||
-webkit-animation: blinkRed 0.5s infinite;
|
||||
-moz-animation: blinkRed 0.5s infinite;
|
||||
-ms-animation: blinkRed 0.5s infinite;
|
||||
-o-animation: blinkRed 0.5s infinite;
|
||||
animation: blinkRed 0.5s infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes blinkRed {
|
||||
from { background-color: #F00; }
|
||||
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||
to { background-color: #F00; }
|
||||
}
|
||||
@-moz-keyframes blinkRed {
|
||||
from { background-color: #F00; }
|
||||
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||
to { background-color: #F00; }
|
||||
}
|
||||
@-ms-keyframes blinkRed {
|
||||
from { background-color: #F00; }
|
||||
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||
to { background-color: #F00; }
|
||||
}
|
||||
@-o-keyframes blinkRed {
|
||||
from { background-color: #F00; }
|
||||
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||
to { background-color: #F00; }
|
||||
}
|
||||
@keyframes blinkRed {
|
||||
from { background-color: #F00; }
|
||||
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||
to { background-color: #F00; }
|
||||
}
|
||||
|
||||
.led-yellow {
|
||||
margin: 0 auto;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #FF0;
|
||||
border-radius: 50%;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 12px;
|
||||
-webkit-animation: blinkYellow 1s infinite;
|
||||
-moz-animation: blinkYellow 1s infinite;
|
||||
-ms-animation: blinkYellow 1s infinite;
|
||||
-o-animation: blinkYellow 1s infinite;
|
||||
animation: blinkYellow 1s infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes blinkYellow {
|
||||
from { background-color: #FF0; }
|
||||
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||
to { background-color: #FF0; }
|
||||
}
|
||||
@-moz-keyframes blinkYellow {
|
||||
from { background-color: #FF0; }
|
||||
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||
to { background-color: #FF0; }
|
||||
}
|
||||
@-ms-keyframes blinkYellow {
|
||||
from { background-color: #FF0; }
|
||||
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||
to { background-color: #FF0; }
|
||||
}
|
||||
@-o-keyframes blinkYellow {
|
||||
from { background-color: #FF0; }
|
||||
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||
to { background-color: #FF0; }
|
||||
}
|
||||
@keyframes blinkYellow {
|
||||
from { background-color: #FF0; }
|
||||
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||
to { background-color: #FF0; }
|
||||
}
|
||||
|
||||
.led-green {
|
||||
margin: 0 auto;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #ABFF00;
|
||||
border-radius: 50%;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
|
||||
}
|
||||
|
||||
.led-blue {
|
||||
margin: 0 auto;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #24E0FF;
|
||||
border-radius: 50%;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #006 0 -1px 9px, #3F8CFF 0 2px 14px;
|
||||
}
|
||||
|
||||
table td {
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
table th, table td {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
table td {
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
table th, table td {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
td[Attributes Style] {
|
||||
text-align: -webkit-center;
|
||||
}
|
||||
td {
|
||||
display: table-cell;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
table {
|
||||
margin-bottom: 30px;
|
||||
width: 800px;
|
||||
text-align: left;
|
||||
color: #3f3f3f;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
table {
|
||||
margin-bottom: 30px;
|
||||
width: 800px;
|
||||
text-align: left;
|
||||
color: #3f3f3f;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px;
|
||||
}
|
||||
Reference in New Issue
Block a user