New door-graph, Improved slideshow, moved video
This commit is contained in:
parent
a4ce890a36
commit
6c891b3f79
|
@ -157,11 +157,10 @@ nav #usermenu li:first-child:hover {
|
|||
border-radius: 5px;
|
||||
padding: 8px 8px;
|
||||
margin: 4px 4px;
|
||||
color: white;
|
||||
}
|
||||
#doorIndicator > p > abbr[title] { text-decoration: none; border-bottom: none; cursor: inherit; }
|
||||
.doorIndicator_OPEN { border: 2px solid green; }
|
||||
.doorIndicator_CLOSED { border: 2px dotted red; }
|
||||
.doorStateMobileOnly { display: none; }
|
||||
|
||||
#mazeMapper {
|
||||
width: 90%;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* chartjs-adapter-moment v1.0.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2021 chartjs-adapter-moment Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
|
||||
//# sourceMappingURL=chartjs-adapter-moment.min.js.map
|
|
@ -5,20 +5,38 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Inngangsverkstedet</title>
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
width: 80vw;
|
||||
margin: auto auto;
|
||||
}
|
||||
#graphDiv {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Felixalb 2021 -->
|
||||
<h2>En kort analyse av nerders døgnrytme i deres naturlige habitat, PVV</h2>
|
||||
<h3 id="infoText"></h3>
|
||||
<canvas id="doorGraph1"></canvas>
|
||||
<script src="./p5.min.js"></script>
|
||||
<div id="graphDiv">
|
||||
<h4>Siste 24 timer</h4>
|
||||
<canvas id="doorGraphDay"></canvas>
|
||||
<h4>Siste 7 dager</h4>
|
||||
<canvas id="doorGraphWeek"></canvas>
|
||||
</div>
|
||||
|
||||
<script src="chart.min.js"></script>
|
||||
<script src="moment.js"></script>
|
||||
<script src="chartjs-adapter-moment.js"></script>
|
||||
<script>
|
||||
|
||||
const infoEl = document.getElementById("infoText");
|
||||
const graphEl1 = document.getElementById("doorGraph1");
|
||||
const graphElDay = document.getElementById("doorGraphDay");
|
||||
const graphElWeek = document.getElementById("doorGraphWeek");
|
||||
|
||||
const XHR = new XMLHttpRequest();
|
||||
const url="/door/?period=day&edgeonly=true";
|
||||
const url="/door/?period=week";
|
||||
XHR.open("GET", url);
|
||||
XHR.send();
|
||||
|
||||
|
@ -26,76 +44,102 @@
|
|||
XHR.onreadystatechange = ()=>{
|
||||
if (XHR.readyState == 4 && XHR.status == 200) {
|
||||
console.log("Response 200 from API")
|
||||
response = JSON.parse(XHR.responseText);
|
||||
response = JSON.parse(XHR.responseText); //Should be try-catched?
|
||||
if (response.status != "OK") {
|
||||
infoEl.innerHTML = "Error when connecting to API.";
|
||||
console.log("Error when connecting to API.");
|
||||
return
|
||||
} else {
|
||||
let datapoints = response.entries;
|
||||
console.log("Success, " + datapoints.length + " datapoints received.");
|
||||
// displayLineDiagram(graphEl1, datapoints);
|
||||
displayBar(datapoints);
|
||||
const allDatapoints = response.entries;
|
||||
console.log("Success, " + allDatapoints.length + " datapoints received.");
|
||||
|
||||
const dayDatapoints = getLastDay(allDatapoints);
|
||||
|
||||
displayLineDiagram(graphElDay, dayDatapoints, "hour");
|
||||
displayLineDiagram(graphElWeek, allDatapoints, "day");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function getDateString(time) {
|
||||
// let dateObj = new Date(time*1e3);
|
||||
// return dateObj.toLocaleString();
|
||||
// }
|
||||
function getLastDay(data) {
|
||||
let date = new Date();
|
||||
let curTime = date.getTime();
|
||||
let targetTime = parseInt(curTime/1e3) - (60*60*24);
|
||||
|
||||
// function displayLineDiagram(canv, data) {
|
||||
// let ctx = canv.getContext("2d");
|
||||
// let chart = new Chart(ctx, {
|
||||
// type: 'line',
|
||||
// data: {
|
||||
// labels: data.map(entry=> getDateString(entry.time)),
|
||||
// // labels: data.map(entry=> 1e3 * entry.time),
|
||||
// datasets: [{
|
||||
// data: data.map(entry => entry.open)
|
||||
// }],
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
function setup() {
|
||||
createCanvas(800, 200);
|
||||
noLoop();
|
||||
background(50);
|
||||
let i;
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (data[i].time < targetTime) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return data.slice(0, i);
|
||||
}
|
||||
function draw() {}
|
||||
function displayBar(data) {
|
||||
const fullLength = 60*60*24;
|
||||
// const dateObj = new Date();
|
||||
const curTime = Math.floor(Date.now() / 1000)
|
||||
let borderPositions = [0];
|
||||
|
||||
//Convert timestamps to a position on the graph
|
||||
for(let i = data.length-1; i > 0; i--) {
|
||||
const ts = data[i]["time"];
|
||||
const pixelPos = width - (((curTime - ts) / fullLength) * width);
|
||||
borderPositions.push(pixelPos);
|
||||
}
|
||||
function displayLineDiagram(canv, data, timeunit) {
|
||||
let ctx = canv.getContext("2d");
|
||||
let dotColor = data.map(entry => entry.open ? "rgb(10, 150, 10)" : "rgb(200, 100, 100)");
|
||||
|
||||
console.log(borderPositions);
|
||||
|
||||
let sectionColors = ["gray"];
|
||||
//Define list of colors, gray=?, green=open, red=closed
|
||||
for(let i = 0; i < data.length; i++) {
|
||||
sectionColors.push((data[i]["open"]) ? "green" : "red");
|
||||
}
|
||||
console.log(sectionColors);
|
||||
for(let i = 0; i < borderPositions.length-1; i++) {
|
||||
fill(sectionColors[i]);
|
||||
rect(borderPositions[i], 0, borderPositions[i+1], height);
|
||||
console.log(`${sectionColors[i]} from ${borderPositions[i]}px to ${borderPositions[i+1]}px`)
|
||||
}
|
||||
let chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.map(entry=> 1e3 * entry.time),
|
||||
datasets: [{
|
||||
data: data.map(entry => entry.open),
|
||||
stepped: "before",
|
||||
borderColor: dotColor,
|
||||
backgroundColor: dotColor
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxis: {
|
||||
type: "time",
|
||||
time: {
|
||||
unit: timeunit
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
suggestedMin: -0.1,
|
||||
suggestedMax: 1.1,
|
||||
grid: {display: false},
|
||||
ticks: {
|
||||
callback: function(label, index, labels) {
|
||||
if (label == 0) {
|
||||
return "Stengt";
|
||||
} else if (label == 1) {
|
||||
return "Åpent";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(tooltipItem) {
|
||||
const value = tooltipItem.formattedValue;
|
||||
if (value == 0) {
|
||||
return "Stengt";
|
||||
} else if (value == 1) {
|
||||
return "Åpent";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!-- <script src="./chart.min.js"></script> -->
|
||||
</body>
|
||||
</html>
|
|
@ -38,7 +38,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
}
|
||||
|
||||
$lines = $door->getEntriesAfter($startTime);
|
||||
if (isset($_GET["period"]) && (bool)htmlspecialchars($_GET["edgeonly"])) {
|
||||
if (isset($_GET["edgeonly"]) && (bool)htmlspecialchars($_GET["edgeonly"])) {
|
||||
//Ignore repeats
|
||||
$lines = getChanges($lines);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,32 @@
|
|||
const SLIDESHOWDELAYMS = 3500;
|
||||
|
||||
//Defined in slideshow.php: const slideshowFnames
|
||||
|
||||
let slideshowIndex = 1;
|
||||
let slideshowInterval;
|
||||
|
||||
let ssi1 = document.getElementById("slideshowImage1");
|
||||
let ssi2 = document.getElementById("slideshowImage2");
|
||||
|
||||
|
||||
function stepSlideshow(imgs) {
|
||||
//Swap image elements
|
||||
let tmp = ssi1;
|
||||
ssi1 = ssi2;
|
||||
ssi2 = tmp;
|
||||
//Swap visibility
|
||||
ssi2.classList.remove("slideshowactive");
|
||||
ssi1.classList.add("slideshowactive");
|
||||
setTimeout(()=>{
|
||||
//Change source to next picture after it is faded out
|
||||
slideshowIndex = (slideshowIndex + 1) % imgs.length;
|
||||
ssi2.src = slideshowFnames[slideshowIndex];
|
||||
}, 800);
|
||||
}
|
||||
|
||||
//Initialize slideshow, start interval
|
||||
if (slideshowFnames.length > 1) {
|
||||
slideshowInterval = setInterval(()=>{
|
||||
stepSlideshow(slideshowFnames);
|
||||
}, SLIDESHOWDELAYMS);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
//Short path to search folder, full to display in <img>
|
||||
$relativePath = "/bilder/slideshow/";
|
||||
$absolutePath = "/galleri" . $relativePath;
|
||||
//Path to first image in slideshow and fallback image if no others are present
|
||||
$splashImg = "/PNG/PVV-logo-big-bluebg.png";
|
||||
|
||||
$filenames = sCaNdIr(__DIR__ . $relativePath);
|
||||
|
||||
//Remove the expected non-images
|
||||
foreach($filenames as $k => $value) {
|
||||
if(in_array($value, [".gitkeep", ".", ".."])) {
|
||||
unset($filenames[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
function getFullPath($fname) { return ($GLOBALS["absolutePath"] . $fname ); }
|
||||
|
||||
//Sort filenames alphabetically and prepend the path prefix to each item.
|
||||
asort($filenames);
|
||||
$slideshowimagefilenames = aRrAy_MaP("getFullPath", $filenames);
|
||||
|
||||
//Prepend the cover photo
|
||||
ArRaY_uNsHiFt($slideshowimagefilenames, $splashImg);
|
||||
|
||||
eChO('<img class="slideshowimg slideshowactive" id="slideshowImage1" src="' . $slideshowimagefilenames[0] . '">');
|
||||
ecHo('<img class="slideshowimg" id="slideshowImage2" src="' . $slideshowimagefilenames[1] . '">');
|
||||
//Store list of file names in a globel JS variable
|
||||
EchO("<script> const slideshowFnames =" . jSoN_eNcOdE($slideshowimagefilenames) . "; </script>");
|
||||
?>
|
107
www/index.php
107
www/index.php
|
@ -10,9 +10,13 @@ $motd = $motdfetcher->getMOTD();
|
|||
|
||||
$door = new \pvv\side\Door($pdo);
|
||||
$doorEntry = (object)($door->getCurrent());
|
||||
$isDoorOpen = $doorEntry->open;
|
||||
if (date("Y-m-d") == date("Y-m-d", $doorEntry->time)) { $doorTime = date("H:i", $doorEntry->time);
|
||||
} else { $doorTime = date("H:i d/m", $doorEntry->time);}
|
||||
if ($doorEntry->time < (time() - 60*30)) {
|
||||
$doorStateText = "Ingen data fra dørsensor";
|
||||
} else {
|
||||
if ($doorEntry->open) { $doorStateText = "Døren er <b>åpen</b>";
|
||||
} else { $doorStateText = "Døren er <b>ikke åpen</b>"; }
|
||||
}
|
||||
$doorTime = date("H:i", $doorEntry->time);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="no">
|
||||
|
@ -36,96 +40,9 @@ if (date("Y-m-d") == date("Y-m-d", $doorEntry->time)) { $doorTime = date("H:i",
|
|||
</nav>
|
||||
|
||||
<header class="landing">
|
||||
<!-- Statisk bilde:
|
||||
<img class="logo" src="css/logo-white.png"/>
|
||||
-->
|
||||
<!-- Youtube-iframe:
|
||||
<style>
|
||||
.iframe-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
|
||||
}
|
||||
|
||||
/* Then style the iframe to fit in the container div with full height and width */
|
||||
.responsive-iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<div class="iframe-container" style="max-width: 100em;">
|
||||
<iframe class="responsive-iframe" src="https://www.youtube.com/embed/l-iEkaQNQdk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe>
|
||||
</div>
|
||||
-->
|
||||
<div id="imageSlideshow">
|
||||
<?php
|
||||
|
||||
$path = "/galleri/bilder/slideshow/";
|
||||
$splashImg = "/PNG/PVV-logo-big-bluebg.png";
|
||||
|
||||
//Find all files/dirs in folder and discard . and .. directories
|
||||
$filenames = aRrAy_SlIcE(sCaNdIr(__DIR__ . $path), 2);
|
||||
|
||||
function getFullPath($fname) { return ($GLOBALS["path"] . $fname ); }
|
||||
|
||||
//Sort filenames alphabetically and prepend the path prefix to each item.
|
||||
asort($filenames);
|
||||
$slideshowimagefilenames = aRrAy_MaP("getFullPath", $filenames);
|
||||
|
||||
//Prepend the cover photo
|
||||
ArRaY_uNsHiFt($slideshowimagefilenames, $splashImg);
|
||||
|
||||
eChO('<img class="slideshowimg slideshowactive" id="slideshowImage1" src="' . $slideshowimagefilenames[0] . '">');
|
||||
ecHo('<img class="slideshowimg" id="slideshowImage2" src="' . $slideshowimagefilenames[1] . '">');
|
||||
|
||||
//Store list of file names in a globel JS variable
|
||||
EchO("<script> const slideshowFnames =" . jSoN_eNcOdE($slideshowimagefilenames) . "; </script>");
|
||||
?>
|
||||
|
||||
<script>
|
||||
const SLIDESHOWDELAYMS = 3500; //Minimum 3 * fade time (3*800=2400ms)
|
||||
|
||||
let slideshowIndex = 1;
|
||||
let slideshowInterval;
|
||||
|
||||
const ssi1 = document.getElementById("slideshowImage1");
|
||||
const ssi2 = document.getElementById("slideshowImage2");
|
||||
|
||||
function stepSlideshow(imgs) {
|
||||
//Change visible picture, ssi2 active, fades with css
|
||||
ssi1.classList.remove("slideshowactive");
|
||||
ssi2.classList.add("slideshowactive");
|
||||
|
||||
setTimeout(()=>{
|
||||
//Change to ssi1 active, no visible change
|
||||
ssi1.src = ssi2.src;
|
||||
ssi1.classList.add("slideshowactive");
|
||||
|
||||
// Hide ssi2 after ssi1 has appeared, no visible change
|
||||
setTimeout(()=>{
|
||||
ssi2.classList.remove("slideshowactive");
|
||||
}, 800);
|
||||
|
||||
//Prepare for next cycle, no visible change
|
||||
setTimeout(()=>{
|
||||
slideshowIndex = (slideshowIndex + 1) % imgs.length;
|
||||
ssi2.src = imgs[slideshowIndex];
|
||||
}, 1600);
|
||||
}, 800);
|
||||
}
|
||||
//Initialize slideshow, start interval
|
||||
if (slideshowFnames.length > 1) {
|
||||
slideshowInterval = setInterval(()=>{
|
||||
stepSlideshow(slideshowFnames);
|
||||
}, SLIDESHOWDELAYMS);
|
||||
}
|
||||
</script>
|
||||
<?php include("galleri/slideshow.php"); ?>
|
||||
<script src="galleri/slideshow.js"></script>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
|
@ -136,9 +53,9 @@ if (date("Y-m-d") == date("Y-m-d", $doorEntry->time)) { $doorTime = date("H:i",
|
|||
<a class="btn" href="om/"><li>Om PVV</li></a>
|
||||
<a class="btn focus" href="paamelding/"><li>Bli medlem!</li></a>
|
||||
<a class="btn" href="https://use.mazemap.com/#config=ntnu&v=1&zlevel=2¢er=10.406281,63.417093&zoom=19.5&campuses=ntnu&campusid=1&sharepoitype=poi&sharepoi=38159&utm_medium=longurl">Veibeskrivelse</li></a>
|
||||
<div id="doorIndicator" class="<?php echo($isDoorOpen ? "doorIndicator_OPEN" : "doorIndicator_CLOSED"); ?>">
|
||||
<p class="doorStateText"><abbr title="Oppdatert <?php echo($doorTime) ?>">Døren er <b><?php echo($isDoorOpen ? "" : "ikke") ?> åpen</b>.</abbr></p>
|
||||
<p class="doorStateTime doorStateMobileOnly">(Oppdatert <?php echo($doorTime) ?>)</p>
|
||||
<div id="doorIndicator" class="<?php echo($doorEntry->open ? "doorIndicator_OPEN" : "doorIndicator_CLOSED"); ?>" onclick="location.href='/door/graph.html'">
|
||||
<p class="doorStateText"><?php echo($doorStateText) ?></p>
|
||||
<p class="doorStateTime">(Oppdatert <?php echo($doorTime) ?>)</p>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -26,6 +26,31 @@ p {hyphens: auto;}
|
|||
<p>Velkommen til Programvareverkstedets nettside. Programvareverkstedet (PVV) er en studentorganisasjon ved Norges Teknisk-Naturvitenskapelige Universitet (NTNU). PVVs formål er å skape et miljø for datainteresserte personer tilknyttet universitetet. Nåværende og tidligere studenter ved NTNU, samt ansatte ved NTNU og tilstøtende miljø, kan bli medlemmer.</p>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<style>
|
||||
.iframe-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
|
||||
}
|
||||
|
||||
/* Then style the iframe to fit in the container div with full height and width */
|
||||
.responsive-iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<div class="iframe-container" style="max-width: 100em;">
|
||||
<iframe class="responsive-iframe" src="https://www.youtube.com/embed/l-iEkaQNQdk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Hva betyr det å være et medlem av PVV?</h2>
|
||||
|
||||
|
|
Loading…
Reference in New Issue