Compare commits

..

21 Commits

Author SHA1 Message Date
rxf
f6e9f0fef6 DB4ls_all: bei DELTEONE den TYP mit auswerten
anmeld.js: den TYP mit übergeben
Version angepasst
2026-01-20 15:17:13 +01:00
rxf
b77cb63758 sofue: Eintrag in Kalender auch bei 'offen' 2026-01-19 17:15:44 +01:00
rxf
eb46fd0d6d Tippfehler 'jetz' verbessert 2026-01-14 19:05:10 +01:00
rxf
681cf70341 Richtiges Abspeichern des Absagedatums 2026-01-11 09:18:21 +01:00
rxf
01a51a8ed1 Absage-Datum checken 2025-12-24 16:47:37 +01:00
rxf
0e0263abbb Merge branch 'SoFue-mit-Vanilla-javascript' into main 2025-12-24 10:07:52 +01:00
rxf
a8967c707a Version angepasst 2025-12-22 10:32:36 +01:00
rxf
77dc29fa9c Autovervollständigung klappt nun wieder 2025-12-22 10:30:27 +01:00
rxf
2e8e010ceb DB4js_all.php an POHP7.3 angepasst 2025-12-22 10:00:47 +01:00
rxf
61ace47270 Texte bei SoFü angepasst in DB4js_all.php 2025-12-20 20:07:15 +01:00
rxf
66953de7f4 Version updated 2025-12-20 16:59:15 +01:00
rxf
4297a98511 DB4ja_all nicht mehr verwendet
db4js.php wieder auf alte Version
Ebenso bei kalender in index von kalender
2025-12-03 11:18:45 +01:00
rxf
ba026bda31 umgestellt auf DB4js_all 2025-12-01 19:11:46 +01:00
rxf
8fe9086205 den echten BEOs benachrichtigen, zusötzlich im cc rexfue@gmail.com 2025-11-22 15:00:23 +01:00
rxf
45eeb5a9c9 dito 2025-11-17 21:32:11 +01:00
rxf
ca5bc00187 Jetzt passt auch sonneführung 2025-11-17 21:31:48 +01:00
rxf
07f0711ea0 Anmeldung fpr 'regular' funktionert nun 2025-11-17 18:34:50 +01:00
rxf
e009a16972 Überflüssige Files entfernt
DB4js_all.php weiter angepasst
2025-11-17 17:24:55 +01:00
rxf
b78831266d .gitignore dazu 2025-11-17 14:28:24 +01:00
rxf
3e8b1f9691 DB4js als _all zusammengefasst
Beoanswer ist nun OK
2025-11-17 14:22:52 +01:00
rxf
ad0f7b2912 storno an gemneinsame DB angepasst - **** WOP **** 2025-11-10 20:57:34 +01:00
56 changed files with 2546 additions and 5767 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ download
*.log *.log
webseiten webseiten
sternwarte/beoanswer/.env.production

30
sternwarte/.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Environment variables
sternwarte/beoanswer/.env*
# CORS-Proxy Konfiguration (enthält Credentials)
cors-config.php
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -61,7 +61,11 @@ function getOneBEO($kurz, $what)
{ {
global $db; global $db;
$sql_stmt = "SELECT $what FROM beos where kürzel='$kurz'"; if ($what == 'true') {
$sql_stmt = "SELECT * FROM beos where name='$kurz'";
} else {
$sql_stmt = "SELECT $what FROM beos where kürzel='$kurz'";
}
$erg = array(); $erg = array();
$result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db)); $result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db));
$data = mysqli_fetch_assoc($result); $data = mysqli_fetch_assoc($result);
@@ -118,7 +122,7 @@ function getTeilnehmer($seed,$isid,$withdate)
// Daten aller Teilnehmer ab eines Führungsdatumns abholen // Daten aller Teilnehmer ab eines Führungsdatumns abholen
// Parameter: // Parameter:
// $fid: Führungsdatum, ab dem die Info geholt wird // $fid: Führungsdatum, ab dem die Info geholt wirdTeilnehmer - ID
// Return: // Return:
// Dict mit allen Daten des Teilnehmers // Dict mit allen Daten des Teilnehmers
function getAllTeilnehmer($fdatum) function getAllTeilnehmer($fdatum)
@@ -189,7 +193,7 @@ function getNextFuehrungen($soviel, $fid) {
function updateTeilnehmer_fdate($id, $fdatum, $fid) { function updateTeilnehmer_fdate($id, $fdatum, $fid) {
global $db; global $db;
$sql_stmt = "UPDATE anmeldungen SET fdatum=$fdatum,fid=$fid, abgesagt=NULL where id=$id"; $sql_stmt = "UPDATE anmeldungen SET fdatum=$fdatum,fid=$fid where id=$id";
$result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db)); $result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db));
return $result; return $result;
} }
@@ -418,6 +422,23 @@ function getOneRecordTermin($termin) {
} }
// Ein Record von den Sonderführungen mit der ID $id holen und kompleet übermitteln
function getOneSonderTeilnehmer($id) {
global $db;
$query = "select * from SoFue2 where id = $id";
$result = mysqli_query($db, $query) or die(mysqli_error($db));
while ($row = mysqli_fetch_assoc($result)) {
foreach ($row as $key => $value) {
$entry[$key] = $value;
}
$erg[] = $entry;
}
return $erg;
}
$_POST = json_decode(file_get_contents('php://input'), true); $_POST = json_decode(file_get_contents('php://input'), true);
$erg = ""; $erg = "";
@@ -543,6 +564,10 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
case 'GET_ONETERMIN': case 'GET_ONETERMIN':
$erg = getOneRecordTermin($_POST["termin"]); $erg = getOneRecordTermin($_POST["termin"]);
break; break;
case 'GET_ONESONDERTEILNEHMER':
$erg = getOneSonderTeilnehmer($_POST["id"]);
break;
default: default:
$erg = ['error' => 'Unknown POST-Command', 'cmd' => $cmd, 'params' => $x]; $erg = ['error' => 'Unknown POST-Command', 'cmd' => $cmd, 'params' => $x];
} }

1440
sternwarte/DB4js_all.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,669 +0,0 @@
/*! SWFObject v2.0 <http://code.google.com/p/swfobject/>
Copyright (c) 2007 Geoff Stearns, Michael Williams, and Bobby van der Sluis
This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject = function() {
var UNDEF = "undefined",
OBJECT = "object",
SHOCKWAVE_FLASH = "Shockwave Flash",
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
FLASH_MIME_TYPE = "application/x-shockwave-flash",
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
win = window,
doc = document,
nav = navigator,
domLoadFnArr = [],
regObjArr = [],
timer = null,
storedAltContent = null,
storedAltContentId = null,
isDomLoaded = false,
isExpressInstallActive = false;
/* Centralized function for browser feature detection
- Proprietary feature detection (conditional compiling) is used to detect Internet Explorer's features
- User agent string detection is only used when no alternative is possible
- Is executed directly for optimal performance
*/
var ua = function() {
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF && typeof doc.appendChild != UNDEF && typeof doc.replaceChild != UNDEF && typeof doc.removeChild != UNDEF && typeof doc.cloneNode != UNDEF,
playerVersion = [0,0,0],
d = null;
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
d = nav.plugins[SHOCKWAVE_FLASH].description;
if (d) {
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
playerVersion[2] = /r/.test(d) ? parseInt(d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
}
}
else if (typeof win.ActiveXObject != UNDEF) {
var a = null, fp6Crash = false;
try {
a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
}
catch(e) {
try {
a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".6");
playerVersion = [6,0,21];
a.AllowScriptAccess = "always"; // Introduced in fp6.0.47
}
catch(e) {
if (playerVersion[0] == 6) {
fp6Crash = true;
}
}
if (!fp6Crash) {
try {
a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
}
catch(e) {}
}
}
if (!fp6Crash && a) { // a will return null when ActiveX is disabled
try {
d = a.GetVariable("$version"); // Will crash fp6.0.21/23/29
if (d) {
d = d.split(" ")[1].split(",");
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
catch(e) {}
}
}
var u = nav.userAgent.toLowerCase(),
p = nav.platform.toLowerCase(),
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
ie = false,
windows = p ? /win/.test(p) : /win/.test(u),
mac = p ? /mac/.test(p) : /mac/.test(u);
/*@cc_on
ie = true;
@if (@_win32)
windows = true;
@elif (@_mac)
mac = true;
@end
@*/
return { w3cdom:w3cdom, pv:playerVersion, webkit:webkit, ie:ie, win:windows, mac:mac };
}();
/* Cross-browser onDomLoad
- Based on Dean Edwards' solution: http://dean.edwards.name/weblog/2006/06/again/
- Will fire an event as soon as the DOM of a page is loaded (supported by Gecko based browsers - like Firefox -, IE, Opera9+, Safari)
*/
var onDomLoad = function() {
if (!ua.w3cdom) {
return;
}
addDomLoadEvent(main);
if (ua.ie && ua.win) {
try { // Avoid a possible Operation Aborted error
doc.write("<scr" + "ipt id=__ie_ondomload defer=true src=//:></scr" + "ipt>"); // String is split into pieces to avoid Norton AV to add code that can cause errors
var s = getElementById("__ie_ondomload");
if (s) {
s.onreadystatechange = function() {
if (this.readyState == "complete") {
this.parentNode.removeChild(this);
callDomLoadFunctions();
}
};
}
}
catch(e) {}
}
if (ua.webkit && typeof doc.readyState != UNDEF) {
timer = setInterval(function() { if (/loaded|complete/.test(doc.readyState)) { callDomLoadFunctions(); }}, 10);
}
if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, null);
}
addLoadEvent(callDomLoadFunctions);
}();
function callDomLoadFunctions() {
if (isDomLoaded) {
return;
}
if (ua.ie && ua.win) { // Test if we can really add elements to the DOM; we don't want to fire it too early
var s = createElement("span");
try { // Avoid a possible Operation Aborted error
var t = doc.getElementsByTagName("body")[0].appendChild(s);
t.parentNode.removeChild(t);
}
catch (e) {
return;
}
}
isDomLoaded = true;
if (timer) {
clearInterval(timer);
timer = null;
}
var dl = domLoadFnArr.length;
for (var i = 0; i < dl; i++) {
domLoadFnArr[i]();
}
}
function addDomLoadEvent(fn) {
if (isDomLoaded) {
fn();
}
else {
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
}
}
/* Cross-browser onload
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
- Will fire an event as soon as a web page including all of its assets are loaded
*/
function addLoadEvent(fn) {
if (typeof win.addEventListener != UNDEF) {
win.addEventListener("load", fn, false);
}
else if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("load", fn, false);
}
else if (typeof win.attachEvent != UNDEF) {
win.attachEvent("onload", fn);
}
else if (typeof win.onload == "function") {
var fnOld = win.onload;
win.onload = function() {
fnOld();
fn();
};
}
else {
win.onload = fn;
}
}
/* Main function
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
*/
function main() { // Static publishing only
var rl = regObjArr.length;
for (var i = 0; i < rl; i++) { // For each registered object element
var id = regObjArr[i].id;
if (ua.pv[0] > 0) {
var obj = getElementById(id);
if (obj) {
regObjArr[i].width = obj.getAttribute("width") ? obj.getAttribute("width") : "0";
regObjArr[i].height = obj.getAttribute("height") ? obj.getAttribute("height") : "0";
if (hasPlayerVersion(regObjArr[i].swfVersion)) { // Flash plug-in version >= Flash content version: Houston, we have a match!
if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements
fixParams(obj);
}
setVisibility(id, true);
}
else if (regObjArr[i].expressInstall && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) { // Show the Adobe Express Install dialog if set by the web page author and if supported (fp6.0.65+ on Win/Mac OS only)
showExpressInstall(regObjArr[i]);
}
else { // Flash plug-in and Flash content version mismatch: display alternative content instead of Flash content
displayAltContent(obj);
}
}
}
else { // If no fp is installed, we let the object element do its job (show alternative content)
setVisibility(id, true);
}
}
}
/* Fix nested param elements, which are ignored by older webkit engines
- This includes Safari up to and including version 1.2.2 on Mac OS 10.3
- Fall back to the proprietary embed element
*/
function fixParams(obj) {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var e = createElement("embed"), a = nestedObj.attributes;
if (a) {
var al = a.length;
for (var i = 0; i < al; i++) {
if (a[i].nodeName.toLowerCase() == "data") {
e.setAttribute("src", a[i].nodeValue);
}
else {
e.setAttribute(a[i].nodeName, a[i].nodeValue);
}
}
}
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var j = 0; j < cl; j++) {
if (c[j].nodeType == 1 && c[j].nodeName.toLowerCase() == "param") {
e.setAttribute(c[j].getAttribute("name"), c[j].getAttribute("value"));
}
}
}
obj.parentNode.replaceChild(e, obj);
}
}
/* Fix hanging audio/video threads and force open sockets and NetConnections to disconnect
- Occurs when unloading a web page in IE using fp8+ and innerHTML/outerHTML
- Dynamic publishing only
*/
function fixObjectLeaks(id) {
if (ua.ie && ua.win && hasPlayerVersion("8.0.0")) {
win.attachEvent("onunload", function () {
var obj = getElementById(id);
if (obj) {
for (var i in obj) {
if (typeof obj[i] == "function") {
obj[i] = function() {};
}
}
obj.parentNode.removeChild(obj);
}
});
}
}
/* Show the Adobe Express Install dialog
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
*/
function showExpressInstall(regObj) {
isExpressInstallActive = true;
var obj = getElementById(regObj.id);
if (obj) {
if (regObj.altContentId) {
var ac = getElementById(regObj.altContentId);
if (ac) {
storedAltContent = ac;
storedAltContentId = regObj.altContentId;
}
}
else {
storedAltContent = abstractAltContent(obj);
}
if (!(/%$/.test(regObj.width)) && parseInt(regObj.width, 10) < 310) {
regObj.width = "310";
}
if (!(/%$/.test(regObj.height)) && parseInt(regObj.height, 10) < 137) {
regObj.height = "137";
}
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
dt = doc.title,
fv = "MMredirectURL=" + win.location + "&MMplayerType=" + pt + "&MMdoctitle=" + dt,
replaceId = regObj.id;
// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
if (ua.ie && ua.win && obj.readyState != 4) {
var newObj = createElement("div");
replaceId += "SWFObjectNew";
newObj.setAttribute("id", replaceId);
obj.parentNode.insertBefore(newObj, obj); // Insert placeholder div that will be replaced by the object element that loads expressinstall.swf
obj.style.display = "none";
win.attachEvent("onload", function() { obj.parentNode.removeChild(obj); });
}
createSWF({ data:regObj.expressInstall, id:EXPRESS_INSTALL_ID, width:regObj.width, height:regObj.height }, { flashvars:fv }, replaceId);
}
}
/* Functions to abstract and display alternative content
*/
function displayAltContent(obj) {
if (ua.ie && ua.win && obj.readyState != 4) {
// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
var el = createElement("div");
obj.parentNode.insertBefore(el, obj); // Insert placeholder div that will be replaced by the alternative content
el.parentNode.replaceChild(abstractAltContent(obj), el);
obj.style.display = "none";
win.attachEvent("onload", function() { obj.parentNode.removeChild(obj); });
}
else {
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
}
}
function abstractAltContent(obj) {
var ac = createElement("div");
if (ua.win && ua.ie) {
ac.innerHTML = obj.innerHTML;
}
else {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (!(c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "param") && !(c[i].nodeType == 8)) {
ac.appendChild(c[i].cloneNode(true));
}
}
}
}
}
return ac;
}
/* Cross-browser dynamic SWF creation
*/
function createSWF(attObj, parObj, id) {
var r, el = getElementById(id);
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
attObj.id = id;
}
if (ua.ie && ua.win) { // IE, the object element and W3C DOM methods do not combine: fall back to outerHTML
var att = "";
for (var i in attObj) {
if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries, like Object.prototype.toJSONString = function() {}
if (i == "data") {
parObj.movie = attObj[i];
}
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
att += ' class="' + attObj[i] + '"';
}
else if (i != "classid") {
att += ' ' + i + '="' + attObj[i] + '"';
}
}
}
var par = "";
for (var j in parObj) {
if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
}
}
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
fixObjectLeaks(attObj.id); // This bug affects dynamic publishing only
r = getElementById(attObj.id);
}
else if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements: fall back to the proprietary embed element
var e = createElement("embed");
e.setAttribute("type", FLASH_MIME_TYPE);
for (var k in attObj) {
if (attObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries
if (k == "data") {
e.setAttribute("src", attObj[k]);
}
else if (k.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
e.setAttribute("class", attObj[k]);
}
else if (k != "classid") { // Filter out IE specific attribute
e.setAttribute(k, attObj[k]);
}
}
}
for (var l in parObj) {
if (parObj[l] != Object.prototype[l]) { // Filter out prototype additions from other potential libraries
if (l != "movie") { // Filter out IE specific param element
e.setAttribute(l, parObj[l]);
}
}
}
el.parentNode.replaceChild(e, el);
r = e;
}
else { // Well-behaving browsers
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
for (var m in attObj) {
if (attObj[m] != Object.prototype[m]) { // Filter out prototype additions from other potential libraries
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
o.setAttribute("class", attObj[m]);
}
else if (m != "classid") { // Filter out IE specific attribute
o.setAttribute(m, attObj[m]);
}
}
}
for (var n in parObj) {
if (parObj[n] != Object.prototype[n] && n != "movie") { // Filter out prototype additions from other potential libraries and IE specific param element
createObjParam(o, n, parObj[n]);
}
}
el.parentNode.replaceChild(o, el);
r = o;
}
return r;
}
function createObjParam(el, pName, pValue) {
var p = createElement("param");
p.setAttribute("name", pName);
p.setAttribute("value", pValue);
el.appendChild(p);
}
function getElementById(id) {
return doc.getElementById(id);
}
function createElement(el) {
return doc.createElement(el);
}
function hasPlayerVersion(rv) {
var pv = ua.pv, v = rv.split(".");
v[0] = parseInt(v[0], 10);
v[1] = parseInt(v[1], 10);
v[2] = parseInt(v[2], 10);
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
}
/* Cross-browser dynamic CSS creation
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
*/
function createCSS(sel, decl) {
if (ua.ie && ua.mac) {
return;
}
var h = doc.getElementsByTagName("head")[0], s = createElement("style");
s.setAttribute("type", "text/css");
s.setAttribute("media", "screen");
if (!(ua.ie && ua.win) && typeof doc.createTextNode != UNDEF) {
s.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
}
h.appendChild(s);
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
var ls = doc.styleSheets[doc.styleSheets.length - 1];
if (typeof ls.addRule == OBJECT) {
ls.addRule(sel, decl);
}
}
}
function setVisibility(id, isVisible) {
var v = isVisible ? "visible" : "hidden";
if (isDomLoaded) {
getElementById(id).style.visibility = v;
}
else {
createCSS("#" + id, "visibility:" + v);
}
}
function getTargetVersion(obj) {
if (!obj)
return 0;
var c = obj.childNodes;
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "object") {
c = c[i].childNodes;
cl = c.length;
i = 0;
}
if (c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "param" && c[i].getAttribute("name") == "swfversion") {
return c[i].getAttribute("value");
}
}
return 0;
}
function getExpressInstall(obj) {
if (!obj)
return "";
var c = obj.childNodes;
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "object") {
c = c[i].childNodes;
cl = c.length;
i = 0;
}
if (c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "param" && c[i].getAttribute("name") == "expressinstall") {
return c[i].getAttribute("value");
}
}
return "";
}
return {
/* Public API
- Reference: http://code.google.com/p/swfobject/wiki/SWFObject_2_0_documentation
*/
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr) {
if (!ua.w3cdom || !objectIdStr) {
return;
}
var obj = document.getElementById(objectIdStr);
var xi = getExpressInstall(obj);
var regObj = {};
regObj.id = objectIdStr;
regObj.swfVersion = swfVersionStr ? swfVersionStr : getTargetVersion(obj);
regObj.expressInstall = xiSwfUrlStr ? xiSwfUrlStr : ((xi != "") ? xi : false);
regObjArr[regObjArr.length] = regObj;
setVisibility(objectIdStr, false);
},
getObjectById: function(objectIdStr) {
var r = null;
if (ua.w3cdom && isDomLoaded) {
var o = getElementById(objectIdStr);
if (o) {
var n = o.getElementsByTagName(OBJECT)[0];
if (!n || (n && typeof o.SetVariable != UNDEF)) {
r = o;
}
else if (typeof n.SetVariable != UNDEF) {
r = n;
}
}
}
return r;
},
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj) {
if (!ua.w3cdom || !swfUrlStr || !replaceElemIdStr || !widthStr || !heightStr || !swfVersionStr) {
return;
}
widthStr += ""; // Auto-convert to string to make it idiot proof
heightStr += "";
if (hasPlayerVersion(swfVersionStr)) {
setVisibility(replaceElemIdStr, false);
var att = (typeof attObj == OBJECT) ? attObj : {};
att.data = swfUrlStr;
att.width = widthStr;
att.height = heightStr;
var par = (typeof parObj == OBJECT) ? parObj : {};
if (typeof flashvarsObj == OBJECT) {
for (var i in flashvarsObj) {
if (flashvarsObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + i + "=" + flashvarsObj[i];
}
else {
par.flashvars = i + "=" + flashvarsObj[i];
}
}
}
}
addDomLoadEvent(function() {
createSWF(att, par, replaceElemIdStr);
if (att.id == replaceElemIdStr) {
setVisibility(replaceElemIdStr, true);
}
});
}
else if (xiSwfUrlStr && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) {
setVisibility(replaceElemIdStr, false);
addDomLoadEvent(function() {
var regObj = {};
regObj.id = regObj.altContentId = replaceElemIdStr;
regObj.width = widthStr;
regObj.height = heightStr;
regObj.expressInstall = xiSwfUrlStr;
showExpressInstall(regObj);
});
}
},
getFlashPlayerVersion: function() {
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
},
hasFlashPlayerVersion:hasPlayerVersion,
createSWF: function(attObj, parObj, replaceElemIdStr) {
if (ua.w3cdom && isDomLoaded) {
return createSWF(attObj, parObj, replaceElemIdStr);
}
else {
return undefined;
}
},
createCSS: function(sel, decl) {
if (ua.w3cdom) {
createCSS(sel, decl);
}
},
addDomLoadEvent:addDomLoadEvent,
addLoadEvent:addLoadEvent,
getQueryParamValue: function(param) {
var q = doc.location.search || doc.location.hash;
if (param == null) {
return q;
}
if(q) {
var pairs = q.substring(1).split("&");
for (var i = 0; i < pairs.length; i++) {
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
return pairs[i].substring((pairs[i].indexOf("=") + 1));
}
}
}
return "";
},
// For internal usage only
expressInstallCallback: function() {
if (isExpressInstallActive && storedAltContent) {
var obj = getElementById(EXPRESS_INSTALL_ID);
if (obj) {
obj.parentNode.replaceChild(storedAltContent, obj);
if (storedAltContentId) {
setVisibility(storedAltContentId, true);
if (ua.ie && ua.win) {
storedAltContent.style.display = "block";
}
}
storedAltContent = null;
storedAltContentId = null;
isExpressInstallActive = false;
}
}
}
};
}();

View File

@@ -292,7 +292,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " . "$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" . // $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" . ".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n\r\n".
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" . "Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Führungsbeginn \r\n" . "und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Führungsbeginn \r\n" .
"eine Email. Sie können sich dann gerne zu einem neuen Termin anmelden.\r\n\r\n" . "eine Email. Sie können sich dann gerne zu einem neuen Termin anmelden.\r\n\r\n" .
@@ -379,8 +378,7 @@
</p> </p>
<p> <p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf), Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung erhalten Sie eine Anmeldebestätigung per e-mail.
ausgedruckt oder in digitaler Form mitbringen!
<!-- <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt <!-- <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt
k e i n Einlass.</strong></div> k e i n Einlass.</strong></div>
@@ -484,7 +482,7 @@
<p> <p>
Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a> Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a>
</p> </p>
<p class="lastchange">Letzte Änderungen: 2025-10-22 rxf</p> <p class="lastchange">Letzte Änderungen: 2025-12-01 rxf</p>
</form> </form>
</div> </div>

View File

@@ -290,8 +290,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " . "$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" . // $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" . ".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung und 2G-Zertifikat erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" . "Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" . "und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" .
"Die Hygienevorschriften sind zu beachten: die Teilnehmer müssen eine medizinische Maske,\r\n" . "Die Hygienevorschriften sind zu beachten: die Teilnehmer müssen eine medizinische Maske,\r\n" .
@@ -367,8 +365,7 @@
</p> </p>
<p> <p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf), Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung erhalten Sie eine Anmeldebestätigung per e-mail.
ausgedruckt oder in digitaler Form mitbringen!
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung und dem Impfzertifikat erfolgt ausnahmslos <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung und dem Impfzertifikat erfolgt ausnahmslos
k e i n Einlass.</strong></div> k e i n Einlass.</strong></div>

View File

@@ -257,8 +257,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " . "$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" . // $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" . ".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" . "Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" . "und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" .
"Die Hygienevorschriften sind zu beachten: Die Teilnehmer müssen Gesichtsmasken \r\n" . "Die Hygienevorschriften sind zu beachten: Die Teilnehmer müssen Gesichtsmasken \r\n" .
@@ -340,8 +338,7 @@
</p> </p>
<p> <p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit den "Anmeldung senden"-Knopf), Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit den "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung erhalten Sie eine Anmeldebestätigung per e-mail.
ausgedruckt oder in digitaler Form mitbringen!
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos
k e i n Einlass.</strong></div> k e i n Einlass.</strong></div>

View File

@@ -1,5 +1,6 @@
# Production Environment Variables # Production Environment Variables
VITE_API_URL=/intern/sofue/php/sofueDB.php # VITE_API_URL=/intern/sofue/php/sofueDB.php
VITE_API_URL=/DB4js_all.php # Lokales Development Backend (über Proxy)
# HTTP Basic Authentication # HTTP Basic Authentication
VITE_API_USERNAME=beogruppe VITE_API_USERNAME=beogruppe

View File

@@ -1,7 +1,7 @@
{ {
"name": "beoanswer_react", "name": "beoanswer_react",
"private": true, "private": true,
"version": "1.0.2", "version": "1.0.3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -17,8 +17,8 @@ function AppContent() {
const [name, setName] = useState("") const [name, setName] = useState("")
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [mitsend, setMitsend] = useState(false) //const [mitsend, setMitsend] = useState(false)
const [mitback, setMitback] = useState(false) //const [mitback, setMitback] = useState(false)
const version = packageJson.version const version = packageJson.version
const vdate = new Date().toLocaleDateString('de-DE') const vdate = new Date().toLocaleDateString('de-DE')
@@ -66,6 +66,8 @@ function AppContent() {
headers['Authorization'] = `Basic ${credentials}` headers['Authorization'] = `Basic ${credentials}`
} }
console.log(formData)
const response = await fetch(APIURL, { const response = await fetch(APIURL, {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
@@ -160,7 +162,7 @@ function AppContent() {
} }
const setBackButton = () => { const setBackButton = () => {
setMitback(true) // setMitback(true)
} }
// Welche Komponeneten werden angezeigt: // Welche Komponeneten werden angezeigt:

View File

@@ -0,0 +1,14 @@
.modal-content.custom-modal {
width: 95vw; /* Nearly full window width */
max-width: none; /* Remove base max-width constraint */
height: 85vh; /* Tall modal */
max-height: 85vh; /* Cap at viewport height */
display: flex; /* Allow header/body/footer layout */
flex-direction: column;
}
.modal-content.custom-modal .modal-body {
flex: 1; /* Fill remaining space */
overflow: auto; /* Scroll body when content is taller than container */
text-align: left; /* Better for long instructions */
}

View File

@@ -2,6 +2,7 @@ import { useState } from 'react'
import { useFormData } from '../FormContext' import { useFormData } from '../FormContext'
import Modal from './Modal' import Modal from './Modal'
import ConfirmModal from './ConfirmModal' import ConfirmModal from './ConfirmModal'
import './LastButtons.css';
export default function LastButtons({ mitSend, mitBack, handleBack}) { export default function LastButtons({ mitSend, mitBack, handleBack}) {
@@ -13,7 +14,9 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
const [isModalHtml, setIsModalHtml] = useState(false) const [isModalHtml, setIsModalHtml] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false) const [showConfirmModal, setShowConfirmModal] = useState(false)
const [isSuccessModal, setIsSuccessModal] = useState(false) const [isSuccessModal, setIsSuccessModal] = useState(false)
const [isWideModal, setIsWideModal] = useState(false)
const handleSenden = async () => { const handleSenden = async () => {
console.log("Alle Formulardaten: ", formData) console.log("Alle Formulardaten: ", formData)
@@ -37,77 +40,74 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
throw new Error('Keine ID in der URL gefunden.') throw new Error('Keine ID in der URL gefunden.')
} }
// FormData für PHP Backend erstellen // JSON-Objekt statt FormData erstellen
const backendData = new FormData() const backendData = {
backendData.append('cmd', 'UPDATEAFTER') cmd: 'UPDATEAFTER',
backendData.append('id', id) id: id
}
// Formulardaten zu Backend-Feldern mappen // Formulardaten zu Backend-Feldern mappen
// Basis-Status // Basis-Status
if (formData.stattgefunden === 'ja') { if (formData.stattgefunden === 'ja') {
backendData.append('stattgefunden', '1') backendData.stattgefunden = '1'
// Spenden-Informationen // Spenden-Informationen
if (formData.spendenArt) { if (formData.spendenArt) {
switch (formData.spendenArt) { switch (formData.spendenArt) {
case 'bar': case 'bar':
backendData.append('bezahlt', `Kasse ${formData.betrag})`) backendData.bezahlt = `Kasse ${formData.betrag}`
break break
case 'ueber': case 'ueber':
backendData.append('bezahlt', 'Überweisung') backendData.bezahlt = 'Überweisung'
break break
case 'kasse': case 'kasse':
backendData.append('bezahlt', 'Spendenkässle') backendData.bezahlt = 'Spendenkässle'
break break
case 'keine': case 'keine':
backendData.append('bezahlt', 'keine') backendData.bezahlt = 'keine'
break break
} }
} }
} else if (formData.stattgefunden === 'nein') { } else if (formData.stattgefunden === 'nein') {
backendData.append('stattgefunden', '0') backendData.stattgefunden = '0'
backendData.append('bezahlt', 'keine') backendData.bezahlt = 'keine'
// Grund für Ausfall // Grund für Ausfall
if (formData.abgesagt === 'abgesagt') { if (formData.abgesagt === 'abgesagt') {
backendData.append('status', 3) backendData.status = 3
} else if (formData.abgesagt === 'verschoben') { } else if (formData.abgesagt === 'verschoben') {
backendData.append('wtermin', formData.neuesDatum || '1900-01-01 00:00:00') backendData.wtermin = formData.neuesDatum || '1900-01-01 00:00:00'
} }
} }
// Bemerkungen // Bemerkungen
backendData.append('remark', formData.bemerkungen || '') backendData.remark = formData.bemerkungen || ''
// Besucher // Besucher
backendData.append('besucher', formData.besucher || '0') backendData.besucher = formData.besucher || '0'
// // Bearbeitungsdatum setzen // Debug: JSON-Daten loggen
// const now = new Date().toISOString().slice(0, 19).replace('T', ' ') console.log("=== JSON DATA DEBUG ===")
// backendData.append('bearbeitet_am', now)
// Debug: FormData kann nicht direkt geloggt werden, deshalb iterieren
console.log("=== FORM DATA DEBUG ===")
console.log("Original formData aus Context:", formData) console.log("Original formData aus Context:", formData)
console.log("URL ID:", id) console.log("URL ID:", id)
console.log("Backend FormData Inhalt:") console.log("Backend JSON Daten:", JSON.stringify(backendData, null, 2))
for (let [key, value] of backendData.entries()) {
console.log(` ${key}: ${value}`)
}
console.log("========================") console.log("========================")
// HTTP Basic Authentication Header // HTTP Headers mit Basic Authentication und Content-Type
const headers = {} const headers = {
'Content-Type': 'application/json'
}
if (username && password) { if (username && password) {
const credentials = btoa(`${username}:${password}`) const credentials = btoa(`${username}:${password}`)
headers['Authorization'] = `Basic ${credentials}` headers['Authorization'] = `Basic ${credentials}`
} }
// Backend-Aufruf // Backend-Aufruf mit JSON
const response = await fetch(APIURL, { const response = await fetch(APIURL, {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
body: backendData body: JSON.stringify(backendData)
}) })
if (!response.ok) { if (!response.ok) {
@@ -135,9 +135,13 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
responseText.trim() === 'true' responseText.trim() === 'true'
if (isSuccess) { if (isSuccess) {
// E-Mail-Benachrichtigung senden (nicht blockierend)
sendEmailNotification(id, formData, backendData, APIURL, headers)
setModalType('success') setModalType('success')
setModalMessage('✅ Daten erfolgreich gespeichert!') setModalMessage('✅ Daten erfolgreich gespeichert!')
setIsSuccessModal(true) setIsSuccessModal(true)
setIsWideModal(false)
setShowModal(true) setShowModal(true)
} else { } else {
@@ -149,12 +153,93 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setModalType('error') setModalType('error')
setModalMessage(`❌ Fehler beim Speichern: ${error.message}`) setModalMessage(`❌ Fehler beim Speichern: ${error.message}`)
setIsSuccessModal(false) setIsSuccessModal(false)
setIsWideModal(false)
setShowModal(true) setShowModal(true)
} finally { } finally {
setIsSending(false) setIsSending(false)
} }
} }
// E-Mail-Benachrichtigung senden (asynchron, nicht blockierend)
const sendEmailNotification = async (id, formData, backendData, apiUrl, headers) => {
try {
// Details zur Sonderführung laden (für BEO und Besucher-Name)
let beoName = 'unbekannt'
let visitorName = 'unbekannt'
let termin = ''
try {
const detailsResp = await fetch(apiUrl, {
method: 'POST',
headers,
body: JSON.stringify({ cmd: 'GET_ONE', id })
})
if (detailsResp.ok) {
const details = await detailsResp.json()
// Backend kann Objekt oder Array liefern; robust extrahieren
const d = Array.isArray(details) ? (details[0] || {}) : (details || {})
// Felder: mitarbeiter (BEO), name/vorname (Besucher), wtermin (Termin)
beoName = d.mitarbeiter || beoName
const vn = d.vorname || ''
const nn = d.name || ''
visitorName = (vn + ' ' + nn).trim() || visitorName
termin = d.wtermin || ''
}
} catch (e) {
console.warn('Konnte Details für E-Mail nicht laden:', e)
}
// E-Mail-Betreff und Inhalt erstellen
const stattgefunden = formData.stattgefunden === 'ja' ? 'Ja' : 'Nein'
const besucher = formData.besucher || '0'
const spenden = backendData.bezahlt || 'keine'
const bemerkungen = formData.bemerkungen || 'keine'
const subject = `Sonderführung vom ${termin} - Nachbearbeitung`
const body = `Nachbearbeitung für Sonderführung ID ${id}
BEO: ${beoName}
Besucher: ${visitorName}
Termin: ${termin}
Stattgefunden: ${stattgefunden}
Anzahl Besucher: ${besucher}
Spenden: ${spenden}
Bemerkungen: ${bemerkungen}
Status: ${formData.stattgefunden === 'ja' ? 'Durchgeführt' :
formData.abgesagt === 'abgesagt' ? 'Abgesagt' :
formData.abgesagt === 'verschoben' ? `Verschoben auf ${formData.neuesDatum}` :
'Unbekannt'}
Diese E-Mail wurde automatisch vom Nachbearbeitungs-System generiert.
`
// E-Mail-Command an Backend senden
const emailData = {
cmd: 'SEND_CONFIRMATION',
to: 'rxf@gmx.de',
subject: subject,
body: body
}
const emailResponse = await fetch(apiUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(emailData)
})
if (emailResponse.ok) {
console.log('✅ E-Mail-Benachrichtigung erfolgreich gesendet')
} else {
console.warn('⚠️ E-Mail-Benachrichtigung konnte nicht gesendet werden:', emailResponse.status)
}
} catch (error) {
// Fehler beim E-Mail-Versand nicht kritisch - nur loggen
console.warn('⚠️ E-Mail-Benachrichtigung fehlgeschlagen:', error)
}
}
const handleAbbruch = () => { const handleAbbruch = () => {
setShowConfirmModal(true) setShowConfirmModal(true)
} }
@@ -178,41 +263,41 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setShowConfirmModal(false) setShowConfirmModal(false)
} }
const handleAnleitung = () => { const handleAnleitung = async () => {
// Öffne die HTML-Anleitung in einem neuen Fenster/Tab
const anleitungUrl = '/anleitung.html'
const windowFeatures = 'width=800,height=600,scrollbars=yes,resizable=yes,toolbar=no,menubar=no,location=no'
try { try {
// Versuche ein Popup-Fenster zu öffnen // Anleitung soll im großen Modal erscheinen
const anleitungWindow = window.open(anleitungUrl, 'anleitung', windowFeatures) setIsWideModal(true)
// Respect Vite base path in production (vite.config.js base: '/beoanswer/')
// Fallback: Wenn Popup blockiert wird, öffne in neuem Tab const base = (import.meta.env && import.meta.env.BASE_URL) ? import.meta.env.BASE_URL : '/'
if (!anleitungWindow) { const normalizedBase = base.endsWith('/') ? base : base + '/'
window.open(anleitungUrl, '_blank') const url = `${normalizedBase}Anleitung.html?v=${Date.now()}` // cache-bust
// Fetch Anleitung.html relative to app base
let response = await fetch(url)
// Fallback: try relative path without base if first attempt failed
if (!response.ok) {
const fallbackUrl = `Anleitung.html?v=${Date.now()}`
response = await fetch(fallbackUrl)
} }
} catch (error) {
// Letzter Fallback: Als Modal anzeigen if (!response.ok) {
console.warn('Anleitung konnte nicht in neuem Fenster geöffnet werden:', error) throw new Error(`Fehler beim Laden der Anleitung (${response.status}): ${response.url}`)
}
const anleitungContent = await response.text()
// Display the content in the modal
setModalType('info') setModalType('info')
setModalMessage(` setModalMessage(anleitungContent)
📋 Anleitung: setIsModalHtml(true)
setShowModal(true)
1. **Fand statt?** - Wählen Sie "ja" oder "nein" } catch (error) {
console.error('Fehler beim Laden der Anleitung:', error)
2. **Bei "ja":** setModalType('error')
- Anzahl Besucher eingeben setModalMessage('❌ Anleitung konnte nicht geladen werden.')
- Spenden-Art auswählen
- Bei Barspende: Betrag eingeben
- Optional: Bemerkungen
3. **Bei "nein":**
- "abgesagt" oder "verschoben" wählen
- Bei verschoben: neues Datum eingeben
4. **Senden** - Speichert alle Daten im System
`)
setIsModalHtml(false) setIsModalHtml(false)
setIsWideModal(false)
setShowModal(true) setShowModal(true)
} }
} }
@@ -244,6 +329,7 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setModalMessage('') setModalMessage('')
setIsModalHtml(false) setIsModalHtml(false)
setIsSuccessModal(false) setIsSuccessModal(false)
setIsWideModal(false)
} }
} }
@@ -282,6 +368,7 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
onClose={closeModal} onClose={closeModal}
type={modalType} type={modalType}
isHtml={isModalHtml} isHtml={isModalHtml}
className={isWideModal ? 'custom-modal' : ''}
/> />
)} )}

View File

@@ -2,7 +2,19 @@ import React from 'react'
// Import des CSS direkt hier // Import des CSS direkt hier
import './Modal.css' import './Modal.css'
export default function Modal({ isOpen = true, onClose, title, children, message, type = 'info', isHtml = false }) { export default function Modal({
isOpen = true,
onClose,
title,
children,
message,
type = 'info',
isHtml = false,
className,
style,
bodyClassName,
bodyStyle
}) {
if (!isOpen) return null if (!isOpen) return null
const handleOverlayClick = (e) => { const handleOverlayClick = (e) => {
@@ -33,7 +45,7 @@ export default function Modal({ isOpen = true, onClose, title, children, message
// CSS-Klasse basierend auf type // CSS-Klasse basierend auf type
const getModalClass = () => { const getModalClass = () => {
return `modal-content modal-${type}` return `modal-content modal-${type}${className ? ' ' + className : ''}`
} }
const displayTitle = title || getDefaultTitle() const displayTitle = title || getDefaultTitle()
@@ -57,12 +69,12 @@ export default function Modal({ isOpen = true, onClose, title, children, message
return ( return (
<div className="modal-overlay" onClick={handleOverlayClick} onKeyDown={handleKeyDown} tabIndex={0}> <div className="modal-overlay" onClick={handleOverlayClick} onKeyDown={handleKeyDown} tabIndex={0}>
<div className={getModalClass()}> <div className={getModalClass()} style={style}>
<div className="modal-header"> <div className="modal-header">
<h3 className="modal-title">{displayTitle}</h3> <h3 className="modal-title">{displayTitle}</h3>
<button className="modal-close" onClick={onClose}>&times;</button> <button className="modal-close" onClick={onClose}>&times;</button>
</div> </div>
<div className="modal-body"> <div className={`modal-body${bodyClassName ? ' ' + bodyClassName : ''}`} style={bodyStyle}>
{getDisplayContent()} {getDisplayContent()}
</div> </div>
<div className="modal-footer"> <div className="modal-footer">

View File

@@ -1,377 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>beoanswer</title>
<style type="text/css">
body {
font-family: Helvetica, arial, sans-serif;
font-size: 14px;
line-height: 1.6;
padding-top: 10px;
padding-bottom: 10px;
background-color: white;
padding: 30px; }
body > *:first-child {
margin-top: 0 !important; }
body > *:last-child {
margin-bottom: 0 !important; }
a {
color: #4183C4; }
a.absent {
color: #cc0000; }
a.anchor {
display: block;
padding-left: 30px;
margin-left: -30px;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
bottom: 0; }
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
cursor: text;
position: relative; }
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
text-decoration: none; }
h1 tt, h1 code {
font-size: inherit; }
h2 tt, h2 code {
font-size: inherit; }
h3 tt, h3 code {
font-size: inherit; }
h4 tt, h4 code {
font-size: inherit; }
h5 tt, h5 code {
font-size: inherit; }
h6 tt, h6 code {
font-size: inherit; }
h1 {
font-size: 28px;
color: black; }
h2 {
font-size: 24px;
border-bottom: 1px solid #cccccc;
color: black; }
h3 {
font-size: 18px; }
h4 {
font-size: 16px; }
h5 {
font-size: 14px; }
h6 {
color: #777777;
font-size: 14px; }
p, blockquote, ul, ol, dl, li, table, pre {
margin: 15px 0; }
hr {
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
border: 0 none;
color: #cccccc;
height: 4px;
padding: 0;
}
body > h2:first-child {
margin-top: 0;
padding-top: 0; }
body > h1:first-child {
margin-top: 0;
padding-top: 0; }
body > h1:first-child + h2 {
margin-top: 0;
padding-top: 0; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
margin-top: 0;
padding-top: 0; }
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0; }
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
margin-top: 0; }
li p.first {
display: inline-block; }
li {
margin: 0; }
ul, ol {
padding-left: 30px; }
ul :first-child, ol :first-child {
margin-top: 0; }
dl {
padding: 0; }
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px; }
dl dt:first-child {
padding: 0; }
dl dt > :first-child {
margin-top: 0; }
dl dt > :last-child {
margin-bottom: 0; }
dl dd {
margin: 0 0 15px;
padding: 0 15px; }
dl dd > :first-child {
margin-top: 0; }
dl dd > :last-child {
margin-bottom: 0; }
blockquote {
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }
table {
padding: 0;border-collapse: collapse; }
table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
table tr:nth-child(2n) {
background-color: #f8f8f8; }
table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr th :first-child, table tr td :first-child {
margin-top: 0; }
table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }
img {
max-width: 100%; }
span.frame {
display: block;
overflow: hidden; }
span.frame > span {
border: 1px solid #dddddd;
display: block;
float: left;
overflow: hidden;
margin: 13px 0 0;
padding: 7px;
width: auto; }
span.frame span img {
display: block;
float: left; }
span.frame span span {
clear: both;
color: #333333;
display: block;
padding: 5px 0 0; }
span.align-center {
display: block;
overflow: hidden;
clear: both; }
span.align-center > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: center; }
span.align-center span img {
margin: 0 auto;
text-align: center; }
span.align-right {
display: block;
overflow: hidden;
clear: both; }
span.align-right > span {
display: block;
overflow: hidden;
margin: 13px 0 0;
text-align: right; }
span.align-right span img {
margin: 0;
text-align: right; }
span.float-left {
display: block;
margin-right: 13px;
overflow: hidden;
float: left; }
span.float-left span {
margin: 13px 0 0; }
span.float-right {
display: block;
margin-left: 13px;
overflow: hidden;
float: right; }
span.float-right > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: right; }
code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px; }
pre code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent; }
.highlight pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px; }
pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px; }
pre code, pre tt {
background-color: transparent;
border: none; }
sup {
font-size: 0.83em;
vertical-align: super;
line-height: 0;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #555;
vertical-align: middle;
background-color: #fcfcfc;
border: solid 1px #ccc;
border-bottom-color: #bbb;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb
}
* {
-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
body {
width: 854px;
margin:0 auto;
}
}
@media print {
table, pre {
page-break-inside: avoid;
}
pre {
word-wrap: break-word;
}
}
</style>
</head>
<body>
<h1 id="toc_0">Anleitung</h1>
<p>Die Webseite hilft bei der Nachbearbeitung von Sonderführungen auf der Sternwarte. Es werden der Reihe nach die relevanten Fragen gestellt, die dann zu beantworten sind. </p>
<ul>
<li><strong>Fand die Führung statt?</strong><br>
Wird benatwortet durch Klick auf <strong>ja</strong> oder <strong>nein</strong>.<br></li>
</ul>
<p>Falls die Führung statt fand, gehts weiter mit:</p>
<ul>
<li><p><strong>Besucher-Anzahl</strong><br>
Hier bitte die tatsächliche Anzahl der Besucher eintragen. Bestätigen mit <em>Return</em> oder durch Klick auf OK. Danach folgt:</p></li>
<li><p><strong>Eine Spende</strong><br>
Es kann gewählt werden, ob eine Barspende eingegangen ist, ob eine Spende überwiesen wird, ob eine Spende in die Spendenkasse
geworfen wurde oder ob keine Spende ausgemacht wurde (z.B. private Führungen). Falls &quot;<em>ist in bar eingegangen</em>&quot; gewäht wurde, kommt:</p></li>
<li><p><strong>Höhe der Barspende</strong><br>
Hier bitte den Betrag in EUR eingeben und wieder mit <em>Return</em> oder Klich aif OK abschließen. </p>
<p>Als Letztes kann noch eine Bemerkung zu der Führung eingegeben werden: </p></li>
<li><p><strong>Bemerkungen (optional)</strong><br>
Diese kann auch leer bleiben. Allerdings <strong>muss</strong> unbeding mit Klick auf OK abgeschlossen werden, sonst gehts nicht weiter.<br>
Danach werden durch Klick auf den <strong>Senden</strong>-Button die Daten an die Datenbank gesendet.</p></li>
</ul>
<p>Falls die Führung <strong>nicht</strong> stattgefunden hat, gehts weiter mit </p>
<ul>
<li><p><strong>Die Führung wurde</strong><br>
<strong>abgestagt</strong> oder <strong>verschoben</strong><br>
Wird <strong>abgesagt</strong> gewählt, erscheint gleich der <strong>Senden</strong>-Button. Die Führung wird dann in der Datenbank als <em>abgesagt</em> markiert.<br>
Wird <strong>verwschoben</strong> gewählt, so erscheint </p></li>
<li><p><strong>Verschoben auf:</strong><br>
und es kann ein neues Datum (mit Uhrzeit) über die aufpoppende Datums-Auswahl gewählt werden. Nach Abschluss mit OK erscheint nun wieder der <strong>Sende</strong>-Button. Durch Klick darauf wird die Führung mit dem neuen Datum als <em>zugesagt</em> in die Datenbank übernommen. Der verantwortliche BEO erhält eine Erinnerungs-Mail.</p>
<p>Während der gesamten Eingabe-Prozedur kann über den <strong>Abbruch</strong>-Button jederzeit der Vorgang abgebrochen und neu begonnen werden.</p></li>
</ul>
<p>rxf 2019-02-04 </p>
</body>
</html>

View File

@@ -1,41 +0,0 @@
#Anleitung
Die Webseite hilft bei der Nachbearbeitung von Sonderführungen auf der Sternwarte. Es werden der Reihe nach die relevanten Fragen gestellt, die dann zu beantworten sind.
* **Fand die Führung statt?**
Wird benatwortet durch Klick auf **ja** oder **nein**.
Falls die Führung statt fand, gehts weiter mit:
* **Besucher-Anzahl**
Hier bitte die tatsächliche Anzahl der Besucher eintragen. Bestätigen mit *Return* oder durch Klick auf OK. Danach folgt:
* **Eine Spende**
Es kann gewählt werden, ob eine Barspende eingegangen ist, ob eine Spende überwiesen wird oder ob keine Spende ausgemacht wurde (z.B. private Führungen). Falss "*ist in bar eingegangen*" gewäht wurde, kommt:
* **Höhe der Barspende**
Hier bitte den Betrag in EUR eingeben und wieder mit *Return* oder Klich aif OK abschließen.
Als Letztes kann noch eine Bemerkung zu der Führung eingegeben werden:
* **Bemerkungen (optional)**
Diese kann auch leer bleiben. Allerdings **muss** unbeding mit Klick auf OK abgeschlossen werden, sonst gehts nicht weiter.
Danach werden durch Klick auf den **Senden**-Button die Daten an die Datenbank gesendet.
Falls die Führung **nicht** stattgefunden hat, gehts weiter mit
* **Die Führung wurde**
**abgestagt** oder **verschoben**
Wird **abgesagt** gewählt, erscheint gleich der **Senden**-Button. Die Führung wird dann in der Datenbank als *abgesagt* markiert.
Wird **verwschoben** gewählt, so erscheint
* **Verschoben auf:**
und es kann ein neues Datum (mit Uhrzeit) über die aufpoppende Datums-Auswahl gewählt werden. Nach Abschluss mit OK erscheint nun wieder der **Sende**-Button. Durch Klick darauf wird die Führung mit dem neuen Datum als *zugesagt* in die Datenbank übernommen. Der verantwortliche BEO erhält eine Erinnerungs-Mail.
Während der gesamten Eingabe-Prozedur kann über den **Abbruch**-Button jederzeit der Vorgang abgebrochen und neu begonnen werden.
rxf 2018-10-17

View File

@@ -1,176 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nachbearbeitung</title>
<link rel="stylesheet" type="text/css" media="screen" href="../intern/sofue/css/jquery-ui.min.css" />
<link rel="stylesheet" type="text/css" media="screen" href="../intern/sofue/css/ui.jqgrid.css" />
<link rel="stylesheet" type="text/css" media="screen" href="../intern/sofue/css/jquery-ui-timepicker-addon.css" />
<link href="css/basscss-custom.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="css/mystyle.css" />
<script type="text/javascript" src="../intern/sofue/js/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="../intern/sofue/js/i18n/grid.locale-de.js"></script>
<script type="text/javascript" src="../intern/sofue/js/jquery.jqGrid.min.js"></script>
<script type="text/javascript" src="../intern/sofue/js/jquery-ui-1.10.0.custom.min.js"></script>
<script type="text/javascript" src="../intern/sofue/js/jquery-ui-timepicker-addon.js"></script>
<script type="text/javascript" src="../intern/sofue/js/moment.js"></script>
<script type="text/javascript" src="../intern/sofue/js/de.js"></script>
<script type="text/javascript" src="../intern/sofue/js/ajax.js"></script>
<script type="text/javascript" src="js/jquery-ui-slider-access-addon.js"></script>
<script type="text/javascript" src="js/version.js"></script>
<script type="text/javascript" src="js/beoanswer.js"></script>
</head>
<?php
$id = $_GET['id']
?>
<script>
let url_id = <?php echo json_encode($id); ?>;
</script>
<body>
<div id="master" class="container h3 border border-blue m1 bg-darken-1">
<section class="px1">
<h1 id="sofueHead" class="h1 mb0 center">Sonderführung vom <br class="sm-hide"></h1>
<h3 class="h3 mb2 center" id="sofueName">für </h3>
<h2 class="h2 mb3 center bg-yellow">Nachbearbeitung</h2>
<div class="clearfix" >
<div>
<div id="inputstatt" class="mb2 border">
<div class="bold" >Fand die Führung statt?</div>
<div>
<label class="inline">
<input type="radio" id="statt_ja" name="statt" value="ja">
ja
</label>
<label class="inline">
<input type="radio" id="statt_nein" name="statt" value="nein">
nein
</label>
</div>
</div>
<div id="inputabs" class="mb2 border hide">
<div class="bold">Die Führung wurde </div>
<div>
<label class="inline mb2 ">
<input type="radio" id="abges" name="nostatt" value="abgesagt">
abgesagt.
</label>
<label class="inline mb2">
<input type="radio" id="verscho" name="nostatt" value="verschoben">
verschoben.
</label>
</div>
</div>
<div id="verschbn" class="mb2 border hide">
<div class="bold">
<label for="newtermin" class="bold mr2">Verschoben auf:</label>
</div>
<div>
<input type="text" id="newtermin" placeholder="neues Datum wählen">
</div>
</div>
</div>
<div id="besucher" class="mb2 border hide">
<div class="bold">
<label for="beszahl" class="bold mr2">Besucher-Anzahl:</label>
</div>
<div>
<input type="text" id="beszahl" placeholder="Anzahl eingeben">
<button class="btn btn-primary mb1 mr2 ml3 mt1" id="btnOK0">OK</button>
</div>
</div>
<div class="clearfix"></div>
<div id="diespende" class="mb2 border hide">
<div class=" bold">Eine Spende</div>
<div>
<div id="inputspend">
<label class="block"> <input type="radio" id="spendbar"
name="spend" value="bar"> ist in bar eingegangen.
</label>
<label class="block"> <input type="radio"
id="spendueber" name="spend" value="ueberweis"> wird überwiesen.
</label>
<label class="block"> <input type="radio"
id="spendkasse" name="spend" value="kasse"> ist in der Spendenkasse.
</label>
<label class="block mb1"> <input type="radio"
id="spendno" name="spend" value="nospend"> ist nicht vorgesehen.
</label>
</div>
</div>
</div>
<div id="barspend" class="mb2 border hide">
<div class="bold">
<label for="barsp" class="bold mr2">Höhe der Barspende:</label>
</div>
<div>
<input class="mr1" type="text" id="barsp" placeholder="Betrag in EURO">&euro;
<button class="btn btn-primary mb1 mr2 ml3 mt1" id="btnOK1">OK</button>
</div>
</div>
<div class="clearfix"></div>
<div id="remarks" class="mb2 border hide">
<div class="bold">
<label for="remark" class="bold mr2">Bemerkungen (optional):</label>
</div>
<div>
<textarea class="mr1" id="remark" placeholder="Bemerkung"></textarea>
<button class="btn btn-primary mb1 mr2 ml3 mt1" id="btnOK2">OK</button>
</div>
</div>
<div id="fertig" class="mb2 border hide ">
<div class="bold center">
Fertig
</div>
</div>
</div>
<div id="beendet" class="m2 border hide">
<div class="bold center">
Daten in die Datenbank übernommen. <br />
Vielen Dank
</div>
</div>
</section>
<div class="center mt2" id="tosend">
<button class="btn btn-primary mb1 mr2" id="btncancel">Abbruch</button>
<button class="btn btn-primary mb1 mr2" id="btnMan">Anleitung</button>
<button class="btn btn-primary mb1 hide" id="btnsend">Senden</button>
</div>
<!-- Ende von "main" -->
<!-- div für PopUp -->
<div id="maint"></div>
<!-- Info unter Tabelle -->
<div id="author" class="h5 mt2 mb1 py1">
<div id="mailadr" class="left px1">
<a href="mailto:rexfue@gmail.com">mailto:rexfue@gmail.com</a>
</div>
<div id="versn" class="right px1">
</div>
<div class="clearfix"></div>
</div>
<div id='anleitung'></div>
</div>
<!-- container -->
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,53 +0,0 @@
:root {
--height-w0:45px;
--height-w1:60px;
--height-w2:100px;
--container-width: 30em;
--button-color: black;
--button-background-color: aqua;
}
.btn-primary {
color: #000;
background-color: skyblue;
border-radius: 3px;
}
#master {
border-width: 2px;
}
#author {
border-top: 1px blue solid;
}
.container {
max-width: 30em;
}
#remark {
width: 200px;
/* border: solid 1px blue; */
}
textarea {
line-height: 1.1;
padding: .5rem .5rem;
}
#remarks, #beszahl, #barsp {
/* border: solid 1px green; */
/* float: left; */
}
#btnOK2, #btnOK1, #btnOK0 {
/* border: solid 1px red; */
/* float: left; */
}
#diespende {
clear: both;
}

View File

@@ -1,318 +0,0 @@
/*
*
*******************
2025-06-16:
Da leider kein CRON auf dem Webserver läuft, kann 'checkfuehrung' bis auf
Weiteres nicht weiter gepflegt bzw. ausgeführt werden. 'checkfuehrung' ist
für dieses Programm )beoanswer) hier notwendig.
*******************
*/
$(document).ready(function() {
let sendobject = {
stattgefunden: false,
anzahl: 0,
spende:0,
betrag:0,
rem:'',
newtermin: '1900-01-01',
};
let ajaxURL = '../../intern/sofue/php/sofueDB.php'
$('#versn').html("Version: " + VERSION + ' vom ' + VDATE);
console.log("von php:", url_id);
// Daten der Führung holen
if(url_id != null) {
doAjaxCall_arr(ajaxURL,{cmd: 'GET_ONE', id: url_id}, enterajaxerg);
} else {
alert("Keine ID übergeben");
return false
// throw new Error("Keine ID übergeben");
}
function enterajaxerg(erg) {
console.log(erg)
let t = $('#sofueHead').html();
$('#sofueHead').html(t+(erg.wtermin).substr(0,10));
$('#sofueName').html('für ' + erg.name);
}
// Change bei 'stattgefunden'
$('#inputstatt').change(function() {
let s = $('input[name="statt"]:checked').val();
console.log(s);
if(s == "nein") {
showAbsage();
sendobject.stattgefunden=false;
} else if (s=='ja') {
showBesucher();
sendobject.stattgefunden=true;
}
});
// der JA-Zweig
function showBesucher() {
$('#besucher').removeClass("hide");
}
$('#beszahl').keydown(e=>{
let keycode = e.keyCode || e.which;
if(keycode == 13) {
besucher = $('#beszahl').val();
if($.isNumeric(besucher)) {
console.log(besucher);
sendobject.anzahl = besucher;
showSpende();
} else {
alert("Nur Ziffern eingeben");
}
}
});
$('#btnOK0').click(()=>{
besucher = $('#beszahl').val();
if($.isNumeric(besucher)) {
console.log(besucher);
sendobject.anzahl = besucher;
showSpende();
} else {
alert("Nur Ziffern eingeben");
}
});
function showSpende() {
$('#diespende').removeClass("hide");
}
$('#inputspend').change(function() {
let spende=0;
let s = $('input[name="spend"]:checked').val();
console.log(s);
if(s == "bar") {
spende=1;
showBarspende();
} else {
showRemark();
if (s=='ueberweis') {
spende=2;
} else if (s=='kasse') {
spende=3;
}
}
sendobject.spende = spende;
});
function showBarspende() {
$('#barspend').removeClass("hide");
}
$('#barsp').keydown(e=>{
let keycode = e.keyCode || e.which;
if(keycode == 13) {
let barsp = $('#barsp').val();
if($.isNumeric(barsp)) {
console.log(barsp);
sendobject.betrag = barsp;
showRemark();
} else {
alert("Nur Ziffern eingeben");
}
}
});
$('#btnOK1').click(()=>{
let barsp = $('#barsp').val();
if($.isNumeric(barsp)) {
console.log(barsp);
sendobject.betrag = barsp;
showRemark();
} else {
alert("Nur Ziffern eingeben");
}
});
$('#btnOK2').click(()=>{
sendobject.rem = $('#remark').val();
showFertig();
});
function showRemark() {
let p = $('#btnOK2').offset();
$('#btnOK2').offset({top: p.top-10, left: p.left});
$('#remarks').removeClass('hide');
}
function showFertig() {
$('#fertig').removeClass("hide");
$('#btnsend').removeClass("hide");
}
// der NEIN-Zweig
function showAbsage() {
$('input[name="nostatt"]').prop('checked',"");
$('#inputabs').removeClass("hide");
}
// Change bei 'nicht stattt'
$('#inputabs').change(function() {
let s = $('input[name="nostatt"]:checked').val();
console.log(s);
if(s == "abgesagt") {
showFertig();
} else if (s == "verschoben") {
showVerschoben();
}
});
function showVerschoben() {
$('#verschbn').removeClass("hide");
// Picker für den neuen Termin
$('#newtermin').datetimepicker( // Initialisierung des datetimepickers
{
addSliderAccess: true,
sliderAccessArgs: { touchonly: false },
showOn: 'focus' , // onFocus wird der Picker angezeigt
timeFormat: 'HH:mm',
showMinute: true,
stepMinute: 15,
// showSecond: false,
// showMillisec: false,
// showMicrosec: false,
// showTimezone: false,
hourMin: 13,
dateFormat: "yy-mm-dd", // angezeigt wird in diesem Format
minDate: "+1d", // min Datum: morgen
closeText: "OK", // erst wenn OK geklickt wird, dann gehts weiter
timeText: "Uhrzeit", // Texte dazu
hourText: "Stunde",
onClose: function (dateText, inst) { // was passiert, wenn OK gedrückt wird:
if(dateText != "") {
var dt = moment(dateText).format('YYYY-MM-DD HH:mm');
$("#newtermin").html(dt);
sendobject.newtermin=dt;
showFertig();
}
}
});
}
// Datepicker auf Deutsch einstellen
$.datepicker.regional['de'] = {
closeText: '',
prevText: '<',
nextText: '>',
currentText: '',
monthNames: ['Januar','Februar','M&auml;rz','April','Mai','Juni',
'Juli','August','September','Oktober','November','Dezember'],
monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jun',
'Jul','Aug','Sep','Okt','Nov','Dez'],
dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
dayNamesShort: ['SON','MON','DIE','MIT','DON','FRE','SAM'],
dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
weekHeader: 'KW',
dateFormat: 'yyyy-mm-dd',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''
};
$.datepicker.setDefaults($.datepicker.regional['de']); // diese Einstellung nun übernehmen
// Buttons
$('#btncancel').click(()=>location.reload());
$('#btnsend').click(()=>{
console.log("Sende an Datenbank:");
console.log(sendobject);
clearAll(0);
let cmd = {
stattgefunden: sendobject.stattgefunden ? 1 : 0,
id: url_id,
cmd: 'UPDATEAFTER'
};
if(sendobject.stattgefunden) { // der Termin hat stattgefunden
cmd.besucher = sendobject.anzahl;
cmd.remark = sendobject.rem;
cmd.bezahlt = (function() {
console.log("sendobjc:", sendobject);
if( sendobject.spende == 1 ) {
return `bar Kasse (€${sendobject.betrag})`;
} else if (sendobject.spende == 2) {
return 'Überweisung';
} else if (sendobject.spende == 3) {
return 'Spendenkasse';
} else return 'Keine';
})()
} else { // der Termin hat NICHT stattgefunden
cmd.besucher = sendobject.anzahl;
cmd.remark = '';
cmd.bezahlt = 'Keine';
if(sendobject.newtermin != '1900-01-01') {
cmd.wtermin = sendobject.newtermin; // er wurde verlegt, also gibts einen neuen Wunschtermin
} else {
cmd.status = 3; // nicht stattgefunden -> abgesagt
}
}
console.log(cmd);
if(url_id != null) {
doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
}
});
function clearAll(was) {
$('#tosend').addClass('hide');
$('#inputstatt').addClass('hide');
$('#fertig').addClass('hide');
$('#verschbn').addClass('hide');
$('#inputabs').addClass('hide');
$('#besucher').addClass('hide');
$('#diespende').addClass('hide');
$('#barspend').addClass('hide');
$('#remarks').addClass('hide');
}
function showajaxerg(erg) {
console.log("AjaxErg: ", erg);
$('#beendet').removeClass('hide');
}
// Klick auf den Anleitungs-Button
$('#btnMan').click(function() {
$("#anleitung").dialog('open');
});
// 3. Dialog für die Anleitung
// Dieser hat KEINEN Button (wird über das Schließkreuz beendet) und
// eine etwas kleinere Schrift
$("#anleitung").dialog({
autoOpen: false,
width: 400,
modal: true,
position: {my: 'top', at: 'top', of: window },
title: 'Anleitung',
open:
function() {
$(this).load('beoanswer.html');
},
buttons: [
{
text: "Abbrechen",
click : function() {
$(this).dialog("close");
},
width: 150,
}
],
});
});

View File

@@ -1,103 +0,0 @@
/*
* jQuery UI Slider Access
* By: Trent Richardson [http://trentrichardson.com]
* Version 0.2
* Last Modified: 12/12/2011
*
* Copyright 2011 Trent Richardson
* Dual licensed under the MIT and GPL licenses.
* http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
* http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
*
*/
(function ($) {
$.fn.extend({
sliderAccess: function (options) {
options = options || {};
options.touchonly = options.touchonly !== undefined ? options.touchonly : true; // by default only show it if touch device
if (options.touchonly === true && !("ontouchend" in document))
return $(this);
return $(this).each(function (i, obj) {
var $t = $(this),
o = $.extend({}, {
where: 'after',
step: $t.slider('option', 'step'),
upIcon: 'ui-icon-plus',
downIcon: 'ui-icon-minus',
text: false,
upText: '+',
downText: '-',
buttonset: true,
buttonsetTag: 'span',
speed: 150
}, options),
$buttons = $('<' + o.buttonsetTag + ' class="ui-slider-access">' +
'<button data-icon="' + o.downIcon + '" data-step="-' + o.step + '">' + o.downText + '</button>' +
'<button data-icon="' + o.upIcon + '" data-step="' + o.step + '">' + o.upText + '</button>' +
'</' + o.buttonsetTag + '>');
$buttons.children('button').each(function (j, jobj) {
var $jt = $(this),
timeout = null,
increment = function($jt, $t, e) {
var step = $jt.data('step'),
curr = $t.slider('value'),
newval = curr += step * 1,
minval = $t.slider('option', 'min'),
maxval = $t.slider('option', 'max');
e.preventDefault();
if (newval < minval || newval > maxval)
return;
$t.slider('value', newval);
$t.slider("option", "slide").call($t, null, { value: newval });
};
$jt.button({
text: o.text,
icons: { primary: $jt.data('icon') }
})
.bind('touchstart mousedown', function (e) {
increment($jt, $t, e);
timeout = setInterval(function () {
increment($jt, $t, e);
}, o.speed);
});
$(document).bind('touchend mouseup', function (e) {
clearInterval(timeout);
return e.type == 'touchend';
});
});
// before or after
$t[o.where]($buttons);
if (o.buttonset) {
$buttons.removeClass('ui-corner-right').removeClass('ui-corner-left').buttonset();
$buttons.eq(0).addClass('ui-corner-left');
$buttons.eq(1).addClass('ui-corner-right');
}
// adjust the width so we don't break the original layout
var bOuterWidth = $buttons.css({
marginLeft: (o.where == 'after' ? 10 : 0),
marginRight: (o.where == 'before' ? 10 : 0)
}).outerWidth(true) + 5;
var tOuterWidth = $t.outerWidth(true);
// support "always" hide the slider
if (o.hideSlider == 'always' || (o.hideSlider == 'touch' && ("ontouchend" in document))) {
$t.css('display', 'none');
}
else {
$t.css('display', 'inline-block').width(tOuterWidth - bOuterWidth);
}
});
}
});
})(jQuery);

View File

@@ -1,17 +0,0 @@
// VersiosNummern und -Geschichte
var VERSION="1.2";
var VDATE="2024-11-01";
/* History
Rev. Datum Entwickler
1.2 2024-11-01 rxf
- Nur noch ID zur identifizierung der Führung
- Name des Besuchers mit anzeigen
1.1 2018-12-11 rxf
- kleinere Anpassungen
1.0 2018-09-27 rxf
- Los gehts
*/

View File

@@ -15,20 +15,29 @@ Dieses Programm kann auch die Überwachung machen, dass Einträge in der DB gel
Versions: Versions:
V 1.0.1 2025-11-17 rxf
- Übergabe der Tage bis zu 'gestern' als Commandline Parameter: '-d x'. Ohne -d wird 1 angesetzt.
V 1.0.0 2025-11-15 rxf
- Mit Tricks kann das nun DOCH realisiert werden:
Auf externem Rechner (z.Zt. 'strato_1'; Miet-Server von rxf) wird der cron angestoßen,
der das Programm hier (checkfuehrung.js) auf dem Sternwartemserver aufruft.
Das zugehörige 'beoanswer' ist eine Webseute, die auch hier auf dem Sternwarte-Server
gehostet ist.
V 0.0 2019-02-04 rxf V 0.0 2019-02-04 rxf
- Start - Start
*/ */
"use strict" "use strict"
const DEVELOP=0; // 1 -> Entwicklung 0-> Produktion const DEVELOP=1; // 1 -> Entwicklung 0-> Produktion
const DAYS=2;
const nodemailer = require('nodemailer');
const moment = require('moment'); const moment = require('moment');
const axios = require('axios'); const axios = require('axios');
const mysql = require('mysql2/promise'); const mysql = require('mysql2/promise');
const nodemailer = require('nodemailer');
const beo_Url = 'beoanswer/beoanswer.php?id='; const beo_Url = 'beoanswer/?id=';
const Url = DEVELOP ? 'http://localhost:8081/' : 'https://sternwarte-welzheim.de/'; const Url = DEVELOP ? 'http://localhost:8081/' : 'https://sternwarte-welzheim.de/';
const DB_host = DEVELOP ? 'localhost' : 'localhost'; const DB_host = DEVELOP ? 'localhost' : 'localhost';
const DB_port = DEVELOP ? 3306 : 3306; const DB_port = DEVELOP ? 3306 : 3306;
@@ -82,15 +91,15 @@ async function fetchDatafromDB(conn,termin) {
function send2BEO(info) { function send2BEO(info) {
var mailOptions = { var mailOptions = {
from: 'noreply@sternwarte-welzheim.de', from: 'noreply@sternwarte-welzheim.de',
// to: info.email, to: info.email,
to: 'rexfue@gmail.com', cc: 'rexfue@gmail.com',
subject: 'Sonderführung vom '+info.date, subject: 'Sonderführung vom '+info.date,
text: 'Hallo ' + info.name + '(' + info.email + '),\n\n' text: 'Hallo ' + info.name + ',\n\n'
+ 'Du hattest gestern Führung! ' + 'du hattest gestern Führung! '
+ 'Bitte fülle folgendes Webformular aus:\n\n' + 'Bitte fülle folgendes Webformular aus:\n\n'
+ Url + beo_Url + info.id + Url + beo_Url + info.id
+ '\n\nBitte nur über diesen Link zugreifen (oder exakt abschreiben),\n' + '\n\nBitte nur über diesen Link zugreifen (oder exakt abschreiben),\n'
+ 'da sonst die Zuordnung nicht hergestellt werden kann.\n' + 'da sonst die Zuordnung nicht hergestellt werden kann.\n\n'
+ 'Besten Dank.\n\nGrüße vom Sonderführungsteam' + 'Besten Dank.\n\nGrüße vom Sonderführungsteam'
}; };
@@ -121,4 +130,8 @@ async function main() {
console.log("All done"); console.log("All done");
} }
const argv = require('minimist')(process.argv.slice(2));
const DAYS = argv.d || 1;
main().catch(console.error); main().catch(console.error);

View File

@@ -473,4 +473,4 @@ textarea {
color: red; color: red;
padding: 5px; padding: 5px;
width: 90%; width: 90%;
} }

View File

@@ -0,0 +1,263 @@
# Sternwarte API DB4js_all.php
Vereinheitlichte Backend-API für öffentliche Führungen, Sonderführungen, Mitarbeiter (BEO), Anmeldungen und Statistiken.
## Überblick
Die Datei `DB4js_all.php` bündelt ehemals mehrere Endpunkte:
- `DB4js.php` (öffentliche Führungen + Anmeldungen)
- `sofueDB.php` (Sonderführungen)
- `anmeldDB.php` (Anmeldungen alte Variante)
- `sofianmeldDB.php` (Sonderführungs-Anmeldungen)
- `statisticDB.php` (Statistiken)
Alle Aufrufe erfolgen über einen einzigen HTTP-POST (oder wenige GET) Request an die Datei mit dem Parameter `cmd`.
Antwortformat immer JSON (`Content-Type: application/json; charset=utf-8`).
## Authentifizierung (optional)
Falls die Environment-Variablen `API_USER` und `API_PASS` gesetzt sind, **muss** Basic-Auth verwendet werden.
Header Beispiel:
```
Authorization: Basic base64(API_USER:API_PASS)
```
Ohne gesetzte ENV-Variablen ist die API offen (nur interne Nutzung empfohlen).
## Allgemeines Request-Format
```http
POST /api/DB4js_all.php
Content-Type: application/json
{
"cmd": "GET_ANMELD",
"id": 42
}
```
Erfolgreiche Antworten: HTTP 200. Fehler: passende HTTP-Status (z.B. 400, 401, 404, 422, 500) + `{"error": "Beschreibung"}`.
## Fehlerstruktur
```json
{
"error": "Message",
"...optional": "Zusatzinformationen"
}
```
## Kommandoliste
| Command | Beschreibung |
|---------|--------------|
| PING | Health-Check (liefert Zeitstempel) |
| LIST_COMMANDS | Liefert alle verfügbaren Kommandos mit Beschreibung |
| GET_ANMELD | Liste öffentlicher Anmeldungen für `fid` |
| GET_ONEANMELD | Einzelne Anmeldung per `id` |
| GET_COUNTS | Anzahl Anmeldungen für `fid` |
| GET_COUNTS_DATE | Anzahl Anmeldungen für Datum `date` (YYYY-MM-DD) |
| INSERT_TLN | Neue öffentliche Anmeldung erstellen |
| UPDATE_TLN | Öffentliche Anmeldung ändern |
| DELETE_TLN | Öffentliche Anmeldung löschen |
| GET_SOFIANMELD | Sonderführungs-Anmeldungen; optional `sofue_id` |
| GET_ONESOFIANMELD | Einzelne Sonderführungs-Anmeldung per `id` |
| GET_SOFIANMELD_COUNT | Anzahl Sonderführungs-Anmeldungen pro `sofue_id` |
| INSERT_SOFIANMELD | Neue Sonderführungs-Anmeldung |
| UPDATE_SOFIANMELD | Sonderführungs-Anmeldung ändern |
| DELETE_SOFIANMELD | Sonderführungs-Anmeldung löschen |
| GET_TERMINE | Öffentliche Führungstermine, optional `includeOld` |
| GET_ONETERMIN | Termin per `id` |
| GET_FID | Führungs-ID zu Datum `datum` |
| GET_TIME | Uhrzeit zu Datum `date` + optional `typ` ("sonnen") |
| GET_BEOS | Alle BEOs; optional `onlyguides` und `what` (Spalten) |
| GET_ONEBEO | Einzelner BEO per `name` |
| GET_ONE | Sonderführung per `id` |
| GET_ONETERMIN_SOFUE | Sonderführung per `termin` (Datum) |
| GET_MANY | Gefilterte Sonderführungen (status, rows, page, termin) |
| UPDATE | Standard-Update einer Sonderführung |
| UPDATEAFTER | Nachbearbeitung (stattgefunden, besucher, remark, bezahlt, status) |
| DELETE | Sonderführung Soft-Delete |
| GET_STATISTIK_SOFUE | Monatsstatistik Sonderführungen Jahr `year` |
| GET_STATISTIK_ANMELD | Monatsstatistik öffentliche Führungen Jahr `year` |
| GET_STATISTIK_BEO | BEO-Führungsstatistik Jahr `year` |
| GET_STATISTIK_GESAMT | Gesamtstatistik Jahr `year` |
| SEND_CONFIRMATION | Einfache Text-Mail |
| SENDMAILZUSAGE | Zusage-Mail an Anfragenden |
| SENDMAIL2BEO | Mail an Mitarbeiter (BEO) |
| SENDMAIL2LISTE | Anfrage an Verteilerliste |
| PUT2KALENDER | Placeholder für Kalender-Eintrag |
## Parameter & Beispiele
### 1. Öffentliche Anmeldungen
#### GET_ANMELD
```json
{ "cmd": "GET_ANMELD", "id": 17 }
```
Antwort: Liste von Anmeldungen.
#### INSERT_TLN
Pflichtfelder: `name`, `email`, `anzahl`, `fid`
```json
{
"cmd": "INSERT_TLN",
"name": "Müller",
"email": "mueller@example.com",
"anzahl": 4,
"fid": 17,
"remarks": "Kommt etwas früher"
}
```
Antwort: `{ "success": true, "id": 123 }`
### 2. Sonderführungs-Anmeldungen
#### INSERT_SOFIANMELD
```json
{
"cmd": "INSERT_SOFIANMELD",
"name": "Schule ABC",
"email": "lehrer@schule.de",
"anzahl": 22,
"sofue_id": 55,
"remarks": "Viele Fragen erwartet"
}
```
### 3. Termine
#### GET_TERMINE
```json
{ "cmd": "GET_TERMINE", "includeOld": "false" }
```
### 4. BEOs
#### GET_BEOS
Hinweis: In der Tabelle `beos` heißt die E-Mail-Spalte `email_1`. Aus Kompatibilitätsgründen akzeptiert die API auch `email` und liefert diese als Alias zurück.
```json
{ "cmd": "GET_BEOS", "onlyguides": "true", "what": "id,name,email" }
```
Oder explizit mit Originalspalte:
```json
{ "cmd": "GET_BEOS", "onlyguides": true, "what": "id,name,email_1" }
```
### 5. Sonderführungen
#### GET_MANY
Filterbar über Status/Termin/Pagination.
```json
{ "cmd": "GET_MANY", "status": "2", "rows": 20, "page": 1 }
```
#### UPDATE (Standard)
```json
{
"cmd": "UPDATE",
"id": 55,
"mitarbeiter": "beo_k1",
"status": 2,
"bemerkung": "Bestätigt",
"wtermin": "2025-12-03 19:00:00"
}
```
#### UPDATEAFTER (Nachbearbeitung)
Optional Felder: `stattgefunden`, `besucher`, `remark`, `bezahlt`, `status`, `wtermin`
```json
{
"cmd": "UPDATEAFTER",
"id": 55,
"stattgefunden": 1,
"besucher": 27,
"remark": "Sehr interessiert",
"bezahlt": "Kasse 50€"
}
```
### 6. Statistiken
#### Gesamtstatistik
```json
{ "cmd": "GET_STATISTIK_GESAMT", "year": 2025 }
```
### 7. Mail
#### SEND_CONFIRMATION
```json
{
"cmd": "SEND_CONFIRMATION",
"to": "rxf@gmx.de",
"subject": "Test",
"body": "Hallo Welt"
}
```
## GET Fallback / Health
Ein GET ohne `cmd` liefert einfachen Status:
```http
GET /api/DB4js_all.php
```
Antwort:
```json
{ "status": "ok", "message": "API erreichbar" }
```
## Typische Fehlerfälle
| Status | Ursache | Beispiel |
|--------|---------|---------|
| 401 | Ungültige oder fehlende Basic-Auth | `{ "error": "Unauthorized" }` |
| 422 | Fehlender `cmd` oder Pflichtfelder | `{ "error": "Command missing" }` |
| 404 | Datensatz nicht gefunden | `{ "error": "Not found" }` |
| 500 | Interner Fehler | `{ "error": "Internal error" }` |
## Sicherheit
- Alle DB-Zugriffe über PDO Prepared Statements.
- Optional Basic-Auth.
- Keine direkte Ausgabe interner Fehlermeldungen an Client.
- E-Mail-Versand minimalistisch (kein HTML-Injection-Risiko durch Plaintext).
## Migration Hinweise
| Alt-Datei | Abgedeckt durch | Hinweise |
|----------|-----------------|----------|
| DB4js.php | Commands für öffentliche Anmeldungen & Termine | Parameter unverändert nutzbar |
| sofueDB.php | Sonderführungen & Nachbearbeitung | `UPDATEAFTER` ersetzt alte updateAfter-Version |
| anmeldDB.php | Enthalten in öffentlichen Anmeldungen | Zusammengeführt |
| sofianmeldDB.php | SoFi-Anmeldungen | eigener Command-Namespace |
| statisticDB.php | Statistik-Commands | Jahr-Parameter optional |
## Beispiel: JS Fetch
```js
async function getAnmeldungen(fid) {
const res = await fetch('/api/DB4js_all.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cmd: 'GET_ANMELD', id: fid })
});
return res.json();
}
```
## Beispiel: curl
```bash
curl -X POST https://example.com/api/DB4js_all.php \
-H 'Content-Type: application/json' \
-d '{"cmd":"PING"}'
```
Mit Basic-Auth:
```bash
curl -X POST https://example.com/api/DB4js_all.php \
-u "$API_USER:$API_PASS" \
-H 'Content-Type: application/json' \
-d '{"cmd":"GET_TERMINE"}'
```
## Erweiterung neuer Commands
1. Eintrag in `Commands::MAP` hinzufügen.
2. Switch-Case im Dispatcher erweitern.
3. Validierung & Antwortformat konsistent halten.
## Known Limitations / TODO
- Kein Rate Limiting.
- Kein Pagination bei öffentlichen Anmeldungen (nur Sonderführungen umgesetzt).
- Kein zentraler Logger für Nutzungsstatistik.
- Kalender-Funktion ist Platzhalter.
## Changelog (Unified Version)
- Erstveröffentlichung: Zusammenführung aller Endpoints, PDO, Auth, Validation.
---
Bei Fragen oder für neue Features bitte erweitern oder Issue anlegen.

View File

@@ -101,8 +101,7 @@
Für diese Führungen gelten folgende Regeln Für diese Führungen gelten folgende Regeln
<ul> <ul>
<li>Besucher/innen müssen sich zur Führung online über unsere Homepage <li>Besucher/innen müssen sich zur Führung online über unsere Homepage
www.sternwarte-welzheim.de <strong>anmelden</strong> und erhalten eine Anmeldebestätigung, www.sternwarte-welzheim.de <strong>anmelden</strong>.</li>
die zur Sternführung mitzubringen ist.</li>
<li>Pro Führung sind maximal <strong><?php echo $maxBesucher; ?></strong> Personen zugelassen. <li>Pro Führung sind maximal <strong><?php echo $maxBesucher; ?></strong> Personen zugelassen.
</li> </li>
</ul> </ul>
@@ -112,7 +111,7 @@
</p> </p>
<p><a href="sonnenfuehrung.php">Anmeldung zu einer Sonnenführung</a> <p><a href="sonnenfuehrung.php">Anmeldung zu einer Sonnenführung</a>
</p> </p>
<p class="lastchange">Letzte Änderungen: 2024-05-19 ae/rxf</p> <p class="lastchange">Letzte Änderungen: 2025-12-01 ae/rxf</p>
</div> <!-- end #mainContent --> </div> <!-- end #mainContent -->

View File

@@ -87,7 +87,7 @@ include 'maintenance.php';
Für diese Führungen gelten folgende Regeln Für diese Führungen gelten folgende Regeln
<ul> <ul>
<li>Besucher/innen müssen sich zur Führung online über unsere Homepage <li>Besucher/innen müssen sich zur Führung online über unsere Homepage
www.sternwarte-welzheim.de <strong>anmelden</strong> und erhalten eine Anmeldebestätigung, die zur Sternführung mitzubringen ist.</li> www.sternwarte-welzheim.de <strong>anmelden</strong>.</li>
<li>Pro Führung sind maximal <strong><?php echo $maxBesucher;?></strong> Personen zugelassen.</li> <li>Pro Führung sind maximal <strong><?php echo $maxBesucher;?></strong> Personen zugelassen.</li>
</ul> </ul>
<p><a href="anmeldung.php">Anmeldung zu öffentlichen Führungen <p><a href="anmeldung.php">Anmeldung zu öffentlichen Führungen

View File

@@ -1,10 +1,11 @@
<?php <?php
$typ=$_GET['typ'] ?? 'regular'; $typ=$_GET['typ'] ?? 'regular';
$tit = "";
if ($typ == 'regular') { if ($typ == 'regular') {
$titel = 'Anmeldungen zur regulären Führung'; $titel = 'Anmeldungen zur regulären Führung';
} elseif ($typ == 'sonnen') { } elseif ($typ == 'sonnen') {
$titel = 'Anmeldungen zur Sonnenführung'; $titel = 'Anmeldungen zur Sonnenführung';
$tit = "-Sonne";
} else { } else {
http_response_code(400); http_response_code(400);
die('Ungültiger Typ.'); die('Ungültiger Typ.');
@@ -16,7 +17,7 @@ if ($typ == 'regular') {
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Anmeldungen</title> <title>Anmeldungen<?echo $tit?></title>
<link rel="stylesheet" href="css/anmeld.css"> <!-- Falls du ein Stylesheet hast --> <link rel="stylesheet" href="css/anmeld.css"> <!-- Falls du ein Stylesheet hast -->
</head> </head>
<body> <body>

View File

@@ -25,7 +25,8 @@ document.addEventListener('DOMContentLoaded', async () => {
let absagegrund = ""; let absagegrund = "";
const useDatum = query.datum const useDatum = query.datum
const ajaxURL = "php/anmeldDB.php"; // Unified API endpoint
const ajaxURL = "../../DB4js_all.php";
const ANZAHL_DATES = query.typ === 'sonnen' ? 25 : 50; const ANZAHL_DATES = query.typ === 'sonnen' ? 25 : 50;
bodytext = "" bodytext = ""
betreff = "" betreff = ""
@@ -54,7 +55,7 @@ Beobachtergruppe Sternwarte Welzheim`
let abgesagt = null let abgesagt = null
let actualdate; let actualdate;
let isSmallScreen = false let isSmallScreen = false
let DateTime = luxon.DateTime const DateTime = luxon.DateTime
function $(selector) { function $(selector) {
@@ -69,7 +70,7 @@ Beobachtergruppe Sternwarte Welzheim`
body.typ = query.typ; body.typ = query.typ;
const response = await fetch(ajaxURL, { const response = await fetch(ajaxURL, {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/js'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
return await response.json(); return await response.json();
@@ -79,7 +80,7 @@ Beobachtergruppe Sternwarte Welzheim`
body.typ = query.typ; body.typ = query.typ;
const response = await fetch(ajaxURL, { const response = await fetch(ajaxURL, {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/js'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
return await response.json(); return await response.json();
@@ -98,9 +99,17 @@ Beobachtergruppe Sternwarte Welzheim`
} }
async function storeAbsage(ids) { async function storeAbsage(ids) {
if (!DateTime || typeof DateTime.now !== 'function') {
console.error('Luxon DateTime ist nicht verfügbar');
throw new Error('Datum kann nicht erstellt werden - Luxon nicht geladen');
}
const dt = DateTime.now() const dt = DateTime.now()
const jetzt = dt.toFormat('yyyy-LL-dd HH:mm') const jetzt = dt.toFormat('yyyy-LL-dd HH:mm:ss')
const update = { cmd: 'UPDATE', field: 'abgesagt', ids: ids, values: [`"${jetzt}"`] }; if (!jetzt || jetzt === 'undefined' || jetzt.includes('undefined')) {
console.error('Ungültiges Datum generiert:', jetzt);
throw new Error('Ungültiges Datum erstellt');
}
const update = { cmd: 'UPDATE_TLN_BULK', field: 'abgesagt', ids: ids, values: [jetzt] };
await putToDbase(update); await putToDbase(update);
abgesagt = jetzt abgesagt = jetzt
} }
@@ -111,7 +120,7 @@ Beobachtergruppe Sternwarte Welzheim`
const entry = holit[0]; const entry = holit[0];
return ` return `
<div class="det">${entry.name} ${entry.vorname}<br /></div> <div class="det">${entry.name} ${entry.vorname}<br /></div>
<div class="det">${entry.email}<br /></div> <div class="det"><a href="mailto:${entry.email}">${entry.email}</a><br /></div>
<div class="det">${entry.plz} ${entry.stadt}<br /></div> <div class="det">${entry.plz} ${entry.stadt}<br /></div>
<div class="det">${entry.strasse}<br /></div> <div class="det">${entry.strasse}<br /></div>
<div class="det">${entry.telefon}<br /></div> <div class="det">${entry.telefon}<br /></div>
@@ -121,9 +130,9 @@ Beobachtergruppe Sternwarte Welzheim`
// Teilnehmer aus 'anmeldungen' austragen und den count in 'fdatum1' anpassen // Teilnehmer aus 'anmeldungen' austragen und den count in 'fdatum1' anpassen
const austragen = async (teilnehmer) => { const austragen = async (teilnehmer) => {
let update = {cmd: 'DELETEONE', id: parseInt(teilnehmer.id)} let update = {cmd: 'DELETEONE', id: parseInt(teilnehmer.id), typ: query.typ}
const erg1 = await putToDbase(update) const erg1 = await putToDbase(update)
// update = {cmd: 'UPDATECOUNT',date: parseInt(teilnehmer.fdatum), anzahl: parseInt(teilnehmer.anzahl)} update = {cmd: 'UPDATECOUNT', date: parseInt(teilnehmer.fdatum), anzahl: parseInt(teilnehmer.anzahl)}
const erg2 = await putToDbase(update) const erg2 = await putToDbase(update)
console.log("Storno Ergebisse: ",erg1,erg2) console.log("Storno Ergebisse: ",erg1,erg2)
showAktAnmeldungen(actualdate) showAktAnmeldungen(actualdate)
@@ -139,13 +148,14 @@ Beobachtergruppe Sternwarte Welzheim`
const anmeldungen = await fetchFromDbase({cmd:'GET_ANMELD', id:date}); const anmeldungen = await fetchFromDbase({cmd:'GET_ANMELD', id:date});
let besucher = 0; let besucher = 0;
clearElement($('tbody')); clearElement($('#tabAnmeld tbody'));
for (let e of anmeldungen) { for (let e of anmeldungen) {
besucher += parseInt(e.anzahl); besucher += parseInt(e.anzahl);
liste.emails.push(e.email); liste.emails.push(e.email);
liste.ids.push(e.id); liste.ids.push(e.id);
abgesagt = e.abgesagt ? e.abgesagt.slice(0,16) : null abgesagt = e.abgesagt ? e.abgesagt.slice(0,16) : null
abgesagt = (abgesagt === '1900-01-01 00:00') ? null : abgesagt
// const selected = e.teilgenommen === "1" ? "checked" : ""; // const selected = e.teilgenommen === "1" ? "checked" : "";
const row = document.createElement('tr'); const row = document.createElement('tr');
@@ -189,11 +199,50 @@ Beobachtergruppe Sternwarte Welzheim`
} }
$('#tabAnmeld tbody').appendChild(sumRow); $('#tabAnmeld tbody').appendChild(sumRow);
} }
// Add group participants line below table
let groupInfoDiv = $('#groupInfo');
if (!groupInfoDiv) {
groupInfoDiv = document.createElement('div');
groupInfoDiv.id = 'groupInfo';
groupInfoDiv.style.fontSize = '75%';
groupInfoDiv.style.textAlign = 'center';
groupInfoDiv.style.marginTop = '10px';
$('#tabAnmeld').parentNode.insertBefore(groupInfoDiv, $('#tabAnmeld').nextSibling);
}
groupInfoDiv.innerHTML = '';
if (query.typ === 'regular' && besucher !== 0) {
const groupName = await getGroupForDate(date);
if (groupName) {
const participants = await getGroupParticipants(groupName);
if (participants.length > 0) {
const participantNames = participants.map(p => `${p.vorname} `).join(', ');
groupInfoDiv.innerHTML = `${groupName} -> ${participantNames}`;
}
}
}
bodytext = TEXTE.bodytext(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy")) bodytext = TEXTE.bodytext(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy"))
betreff = TEXTE.subject(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy")) betreff = TEXTE.subject(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy"))
console.log(betreff) console.log(betreff)
} }
async function getGroupForDate(date) {
const dateInfo = await fetchFromDbase({cmd:'GET_DATES', anzahl: 1, date: date});
return dateInfo && dateInfo.length > 0 ? dateInfo[0].grp : null;
}
async function getGroupParticipants(groupName) {
const allBeos = await fetchFromDbase({cmd:'GET_BEOS', onlyguides: true, what: 'vorname,name,gruppe'});
return allBeos.filter(beo => {
if (!beo.gruppe) return false;
// Split gruppe by comma and check for exact match
// This handles cases like "Mo I, Mo II", "A, B", etc.
const groups = beo.gruppe.split(',').map(g => g.trim());
return groups.includes(groupName);
});
}
async function findName(name) { async function findName(name) {
const update = { cmd: 'GET_TEILN_NAME', name: name }; const update = { cmd: 'GET_TEILN_NAME', name: name };
let erg = await putToDbase(update); let erg = await putToDbase(update);
@@ -263,29 +312,63 @@ Beobachtergruppe Sternwarte Welzheim`
const grundIndex = Array.from($all('input[name=grund]')).find(el => el.checked)?.value || 0; const grundIndex = Array.from($all('input[name=grund]')).find(el => el.checked)?.value || 0;
absagegrund = absagegrundListe[grundIndex]; absagegrund = absagegrundListe[grundIndex];
await storeAbsage(liste.ids); // Versuche das Absagedatum zu speichern, aber breche nicht ab wenn es fehlschlägt
let datumGespeichert = false;
try {
await storeAbsage(liste.ids);
datumGespeichert = true;
} catch (error) {
console.error('Fehler beim Speichern des Absagedatums:', error);
// Warnung anzeigen, aber weitermachen
console.warn('Absagedatum konnte nicht gespeichert werden, aber Mail wird trotzdem versendet');
}
// Laufschrift einschalten (falls regular)
if (query.typ !== 'sonnen') { if (query.typ !== 'sonnen') {
await fetch('https://laufschrift.rexfue.de/switch/switch_on') try {
await fetch('https://laufschrift.rexfue.de/switch/switch_on')
} catch (error) {
console.error('Laufschrift konnte nicht eingeschaltet werden:', error);
}
} }
// Mail senden (wichtigster Teil!)
bodyText = bodytext.replace("{absagegrund}", absagegrund); bodyText = bodytext.replace("{absagegrund}", absagegrund);
const mailRet = await fetchFromDbase({ try {
cmd: 'SENDMYMAIL', const mailRet = await fetchFromDbase({
to: ['rexfue@gmail.com'], cmd: 'SENDMYMAIL',
betreff: betreff, to: ['rexfue@gmail.com'],
body: bodyText, betreff: betreff,
bcc: liste.emails body: bodyText,
}); bcc: liste.emails
if (mailRet.error) { });
$('#errortext').innerHTML = mailRet.errortext
if (mailRet.error) {
$('#errortext').innerHTML = mailRet.errortext
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
$('#errordialog').showModal();
return;
}
console.log("Mailret: ", mailRet, "Gesendet an: ", liste.emails)
// Zeige Warnung an, wenn Datum nicht gespeichert werden konnte
if (!datumGespeichert) {
$('#errortext').innerHTML = `<strong>Hinweis:</strong><br>Die Absage-Mail wurde erfolgreich versendet.<br><br>Das Absagedatum konnte jedoch nicht in der Datenbank gespeichert werden. Bitte notieren Sie manuell, dass die Absage gesendet wurde.`;
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
$('#errordialog').showModal();
}
$('#absagen').innerHTML = TEXTE.absagebutton(abgesagt)
$('#absagedialog').close();
} catch (error) {
console.error('Fehler beim Mail-Versand:', error);
$('#errortext').innerHTML = `<strong>Fehler beim Versenden der Absage-Mail:</strong><br>${error.message}`;
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close()) $('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
$('#errordialog').showModal(); $('#errordialog').showModal();
$('#absagedialog').close();
} }
console.log("Mailret: ", mailRet, "Gesendet an: ", liste.emails)
$('#absagen').innerHTML = TEXTE.absagebutton(abgesagt)
$('#absagedialog').close();
}); });
$('#absagedialog-cancel').addEventListener('click', () => { $('#absagedialog-cancel').addEventListener('click', () => {
@@ -382,10 +465,14 @@ Beobachtergruppe Sternwarte Welzheim`
// t -> Uhrzeit // t -> Uhrzeit
// Return // Return
// neu formatierter Datums-String // neu formatierter Datums-String
const bauDate = (w,d,t) => { const bauDate = (w, d, t) => {
let dd = d.replace(/^(\d{4})(\d{2})(\d{2})$/, '$3.$2.$1',); const ds = String(d ?? '');
let dt = w.substr(0,2) + ', ' + dd + ' ' + t.substr(0,2) + ':00'; const ts = String(t ?? '');
return dt; const ws = String(w ?? '');
const dd = ds.replace(/^(\d{4})(\d{2})(\d{2})$/, '$3.$2.$1');
const hh = ts.substr(0, 2);
const ww = ws.substr(0, 2);
return `${ww}, ${dd} ${hh}:00`;
} }

View File

@@ -1,12 +1,26 @@
// VersiosNummern und -Geschichte // VersiosNummern und -Geschichte
const VERSION="1.9.0"; const VERSION="1.12.1";
const VDATE="2025-11-07"; const VDATE="2026-01-20";
/* History /* History
Rev. Datum Entwickler Rev. Datum Entwickler
1.12.1 2026-01-20 rxf
- bei DELETEONE den typ mit übergeben (sonst geht storno bei sonne nicht)
1.12.0 2026-01-11 rxf
- Abmeldedatum richtig als einfacher String mit Sekunden abspeichern
1.11.0 2025-12-24 rxf
- Wenn das Absagedatum ungültig ist, eine Fehlermeldung
erzeugen, aber die Mails trotzdem versenden
1.10.0 2025-11-17 rxf
- als PHP-Interface nun das DB4js_all.php verwendet
- unter Tabelle noch die Namen der Führenden anzeigen
1.9.0 2025-11-07 rxf 1.9.0 2025-11-07 rxf
- Datum der Absge mit in der DB (abgesagt). Wird angezeigt, wenn abgesagt wurde. - Datum der Absge mit in der DB (abgesagt). Wird angezeigt, wenn abgesagt wurde.

34
sternwarte/intern/sofue/js/sofue.js Executable file → Normal file
View File

@@ -2,7 +2,7 @@
//globale Variable //globale Variable
var maint = false; // Wartung?? var maint = false; // Wartung??
var ajaxURL="php/sofueDB.php"; var ajaxURL="../../DB4js_all.php"
var beos = []; var beos = [];
// 0 1 2 3 (4) (5) // 0 1 2 3 (4) (5)
@@ -54,7 +54,7 @@ function buildEntryScreen(id) {
// Übergenen Zeit testen, ob sie mit 1900 oder 0000 startet // Übergenen Zeit testen, ob sie mit 1900 oder 0000 startet
function check1900(termin) { function check1900(termin) {
if (termin.startsWith("1900") || termin.startsWith("0000")) { if (termin === null || termin.startsWith("1900") || termin.startsWith("0000")) {
return true; return true;
} }
return false; return false;
@@ -284,6 +284,7 @@ function addEditbox(detail) {
//bei der Eingabe der BEOs eine Autovervollständigung durchführen //bei der Eingabe der BEOs eine Autovervollständigung durchführen
function buildBeosComplete() { function buildBeosComplete() {
function split( val ) { function split( val ) {
return val.split( /,\s*/ ); return val.split( /,\s*/ );
} }
@@ -291,6 +292,19 @@ function buildBeosComplete() {
return split( term ).pop(); return split( term ).pop();
} }
// Konvertiere beos Array: wenn Objekte, extrahiere die Namen
var beosNames = beos.map(function(item) {
if (typeof item === 'string') {
return item;
} else if (item && item.name) {
return item.name;
} else {
return String(item);
}
});
console.log("buildBeosComplete - Original beos:", beos);
console.log("buildBeosComplete - Converted names:", beosNames);
$( "#curmar" ) $( "#curmar" )
// don't navigate away from the field on tab when selecting an item // don't navigate away from the field on tab when selecting an item
@@ -304,8 +318,11 @@ function buildBeosComplete() {
minLength: 0, minLength: 0,
source: function( request, response ) { source: function( request, response ) {
// delegate back to autocomplete, but extract the last term // delegate back to autocomplete, but extract the last term
response( $.ui.autocomplete.filter( var term = extractLast( request.term );
beos, extractLast( request.term ) ) ); console.log("Searching for:", term);
var filtered = $.ui.autocomplete.filter(beosNames, term);
console.log("Filtered results:", filtered);
response( filtered );
}, },
focus: function() { focus: function() {
// prevent value inserted on focus // prevent value inserted on focus
@@ -551,7 +568,7 @@ function saveSettings() {
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
if(status == 1) { // offen -> mail an Liste if(status == 1) { // offen -> mail an Liste
sendmail2liste(id); sendmail2liste(id, termin);
} }
if (status == 2) { if (status == 2) {
sendmail2beo(id, marb, termin) sendmail2beo(id, marb, termin)
@@ -595,12 +612,15 @@ function showdbase(val) {
} }
// Status wurde auf 'offen' gesetzt -> nun eine Mail an die Liste mit der Anfrage senden // Status wurde auf 'offen' gesetzt -> nun eine Mail an die Liste mit der Anfrage senden
function sendmail2liste(id) { function sendmail2liste(id, termin) {
const liste = 'sternwarte@planetariumsgesellschaft.de'; const liste = 'sternwarte@planetariumsgesellschaft.de';
const cmd = {cmd: 'SENDMAIL2LISTE', id: id, to: liste} let cmd = {cmd: 'SENDMAIL2LISTE', id: id, to: liste}
console.log("Sende mail to Liste"); console.log("Sende mail to Liste");
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
console.log("Mail gesendet"); console.log("Mail gesendet");
// in den Kalender als Platzhalter (mit ??) eintragen
cmd = {cmd: 'PUT2KALENDER', id: id, termin: termin, mitarbeiter: '??'}
doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
} }
// Status wurde auf 'zugesgat' gesetzt -> nun eine Mail an den BEO senden // Status wurde auf 'zugesgat' gesetzt -> nun eine Mail an den BEO senden

View File

@@ -1,17 +1,28 @@
// VersiosNummern und -Geschichte // VersiosNummern und -Geschichte
var VERSION="1.92"; var VERSION="1.95";
var VDATE="2024-09-20"; var VDATE="2026-01-19";
/* History /* History
Rev. Datum Entwickler Rev. Datum Entwickler
1.95 2026-01-19
- Eintrag in den Kalender auch bei 'offen'
1.94 2025-12-22 rxf
- Autocomplete repariert
1.93 2025-12-20 rxf
- Texte in DB4js_all angepasst, so dass sie wie im alten sofue.js erscheinen
- DB4js_all.php nun verwenden
- prüfen auf 'termin === null' ich check1900()
1.92 2024-09-20 rxf 1.92 2024-09-20 rxf
- Probleme mit 'Zusgae sende' behoben - Probleme mit 'Zusgae sende' behoben
- auch bei mehreren BEOs geht nun die Mail richtig - auch bei mehreren BEOs geht nun die Mail richtig
1.91 2024-09-14 rxf 1.91 2024-09-14 rxf
- and den BEOS keine Kontaktdaten mehr senden (die stehen in der Übersichtsseite) - and den BEOS keine Kontaktdaten mehr senden (die stehen in der Übersichtsseite)
1.90 2024-09-02 rxf 1.90 2024-09-02 rxf
- Automatisch Mails senden an Liste bei 'offen' und am BEO und den Anmeldenden bei 'Zusage' - Automatisch Mails senden an Liste bei 'offen' und am BEO und den Anmeldenden bei 'Zusage'

View File

@@ -7,7 +7,7 @@
$(document).ready(function() { $(document).ready(function() {
// Globals: // Globals:
const url = "php/statistic.php"; const url = "../../DB4js_all.php";
const months = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ]; const months = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ];
const colors = { besucher: ['#FFB60A','#FF6702','#E54818'], aktivitaet: ['#D1D8D1','#ABC1C5','#81ACBC'], gesamt: ['#CBD26A','#88AD63','#5A8A40'] }; const colors = { besucher: ['#FFB60A','#FF6702','#E54818'], aktivitaet: ['#D1D8D1','#ABC1C5','#81ACBC'], gesamt: ['#CBD26A','#88AD63','#5A8A40'] };
const barsPerPage = 23; const barsPerPage = 23;

View File

@@ -1,471 +0,0 @@
// anmeldung.js rxf 2020-10.12
// Dynamik für die Anmelde-Seite
//
$(document).ready(() => {
const ajaxURL="../DB4js.php";
const maxPersonen = 10;
const dummyTln = {
name:"Zuname{z}",
vorname:"",
email:"rxf{z}@gmx.de",
telefon:"0711123456789",
plz:"12345",
stadt:"Ort{z}",
strasse:"Straße {z}",
fid: "",
anzahl:1,
teilgenommen: 0,
remarks:"Bemerkung Nr {z}",
abgesagt: 0,
angemeldet: moment().format("YYYY-MM-DD")
};
let aktualTln = {
name:"",
vorname:"",
email:"",
telefon:"",
plz:"",
stadt:"",
strasse:"",
fid: "",
anzahl:1,
teilgenommen: 0,
remarks:"",
abgesagt: 0,
angemeldet: ""
};
const mandatory = {
name:true,
vorname:false,
email:true,
telefon:false,
plz:true,
stadt:true,
strasse:true,
fid: false,
anzahl:false,
teilgenommen: false,
remarks:false,
abgesagt: false,
angemeldet: true
};
let allParticipants = [];
let errtext = "";
// Von der Datenbank Werte abholen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const fetchFromDbase = async (body) => {
const response = await fetch(ajaxURL,{
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// In die Datenbank Werte eintragen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const putToDbase = async (body) => {
const response = await fetch(ajaxURL,{
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// Beim Senden der Anmeldeseite die Nummer der Führung mit schicken
$('form').submit((event) => {
let nr = $('#ftermin').find(':selected');
console.log('nr: ', nr[0].value);
$('#fid').val(nr[0].value);
});
// zum Testen die Anmelde-Seite automatisch ausfüllen
$('#autofillbutton').click(() => {
let rand = Math.floor(Math.random() * 100);
for (let f in dummyTln) {
if (f=='fid') {
continue;
}
let x = dummyTln[f].toString();
aktualTln[f] = x.replace("{z}",rand);
$('#'+f).val(x.replace("{z}",rand));
}
});
// Aus dem rohen Führungsdatum von der Datenbank ein besser
// leserliches Format erzeugen
// Params:
// w -> Wocjentag
// d -> Datum in DBase-Format
// t -> Uhrzeit
// Return
// neu formatierter Datums-String
const bauDate = (w,d,t) => {
let dd = d.replace(/^(\d{4})(\d{2})(\d{2})$/, '$3.$2.$1',);
return w.substr(0,2) + ', ' + dd + ' ' + t;
}
// Aus der Datenbank die nächsten 'n' Führungsdaten holen und
// in den <option>-Tags anzeigen.
// Zusätzlich für jeden Tag zählen, wieviel schon angemeldet sind.
// Sind es >= 10, dass diese <option> als "disbaled" markieren.
// Die Anzahl der noch freien Plätze mit anzeigen
async function buildfuehrungdates(n) {
const dates = await fetchFromDbase({cmd:'GET_DATES',anzahl:n, fid:0});
str = "";
for (let d of dates) {
const count = await fetchFromDbase({cmd: 'GET_COUNTS', fid: d.fid});
str += `<option ${count >= maxPersonen ? "disabled" : ""} ` +
`value=${d.fid}> ${bauDate(d.weekday, d.date, d.time)} &nbsp;&nbsp;Frei: ${maxPersonen - count}</>`;
}
$('#fid').append(str);
}
// Mit der fid das Führungsdatum aus der DB holen und in leserliche
// Form umsetzen
// Params:
// fid -> ID des Eintrages in der DB
// Return
// String mit dem Führungsdatum
const getfdateformatted = async (fid) => {
const nxd = await fetchFromDbase({cmd: 'GET_DATES', anzahl: 1, fid: fid});
let newdate = bauDate(nxd[0].weekday, nxd[0].date, nxd[0].time);
return newdate.replace(/(\d+) Uhr/, "um $1 Uhr");
}
// Infomail über die Anmeldung bzw. Stornierung an rexfue@gmail.com senden
// Parameters:
// storno -> true: Stornierung, fals: Anmeldung
const sendInfoMail = async(storno) => {
const fdatum = await getfdateformatted(aktualTln.fid);
const subj = storno ? "Stornierung einer Stern-Führung" : "Neue Anmeldung zu einer Stern-Führung";
const ret = await putToDbase({
cmd: 'SEND_INFO_MAIL',
to: 'rexfue@gmail.com',
subject: subj,
body: '<html>' +
'<body><table>' +
'<tr><td>Name, Vorname</td><td style="text-align: right;">' + aktualTln.name + ', ' + aktualTln.vorname + '</td></tr>' +
'<tr><td>Strasse</td><td style="text-align: right;">' + aktualTln.strasse + '</td></tr>' +
'<tr><td>Ort</td><td style="text-align: right;">' + aktualTln.plz + ', ' + aktualTln.stadt + '</td></tr>' +
'<tr><td>Telefon</td><td style="text-align: right;">' + aktualTln.telefon + '</td></tr>' +
'<tr><td>E-mail</td><td style="text-align: right;">' + aktualTln.email + '</td></tr>' +
'<tr><td>Termin</td><td style="text-align: right;">' + fdatum + '</td></tr>' +
'<tr><td>Personen</td><td style="text-align: right;">' + aktualTln.anzahl + '</td></tr>' +
'<tr><td>Bemerkungen</td><td class="fr">' + aktualTln.remarks + '</td></tr>' +
'</table></body></html>'
});
}
// Bestätigungsmail an den Anmeldeneden senden
// Params:
// storno -> true: Stornierung, fals: Anmeldung
const sendConfirmation = async (storno) => {
const fdatum = await getfdateformatted(aktualTln.fid);
let body = "Sehr geehrte Dame, sehr geehrter Herr, \r\n\r\nhiermit bestätigen wir Ihre "
body += storno ? "Stornierung der " : "";
body += "Anmeldung zu der Führung auf der Sternwarte Welzheim am\r\n\r\n" +
`${fdatum} für ${aktualTln.name} ${aktualTln.vorname} mit ${aktualTln.anzahl} ${aktualTln.anzahl==1?"Person.":"Personen."}` +
"\r\n\r\n";
if (!storno) {
body +=
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n" +
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" +
"Falls Sie den Führungstermin absagen wollen, verwenden Sie bitte folgenden Link \r\n";
body += develop == "true" ?
"http://localhost:8082/anmeldung.php?storno=" :
"https://sternwarte-welzheim.de/anmeldung.php?storno=";
body += md5(aktualTln.email + aktualTln.fid) + "\n\r";
body += "Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" +
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" +
"Die Hygienevorschriften sind zu beachten: Die Teilnehmer/-innen müssen Gesichtsmasken \r\n" +
"tragen und den vorgeschriebenen Abstand halten. " +
"Nicht teilnehmen dürfen Personen, \r\ndie in den letzten vierzehn Tagen Kontakt " +
"mit einem Coronavirus-Infizierten \r\nhatten oder Infektionssymptome zeigen.\r\n\r\n";
}
body += "Mit freundlichen Grüßen\r\n\r\n" +
"Beobachterteam der Sternwarte Welzheim\r\n\r\n" +
"www.sternwarte-welzheim.de"
const ret = await putToDbase({
cmd: 'SEND_MAIL',
to: aktualTln.email,
subject: storno ?
"Stornierung Ihrer Anmeldung zu einer Führung auf der Sternwarte Welzheim" :
"Anmeldung zu einer Führung auf der Sternwarte Welzheim",
body: body,
});
console.log("hier wird die Email gesendet");
sendInfoMail(storno);
}
// MD5-Hash ausrechnen
const md5 = (value) => {
return CryptoJS.MD5(value).toString();
}
// Den neuen Teilnehmer in die DB eintragen
const insertEntry = async () => {
let ret = await putToDbase({cmd: 'INSERT_TLN', data: aktualTln})
sendConfirmation(false);
}
// Den Teilnehmer mit der ID id aus der DB löschen
const deleteEntry = async (id) => {
let ret = await putToDbase({cmd: 'DELETE_TLN', id:id});
return ret;
}
// Den Teilnehmer mit der ID id mit den neuen Daten versorgen
const updateEntry = async (id) => {
let ret = await putToDbase({cmd: 'UPDATE_TLN', data: aktualTln, id: id })
sendConfirmation(false);
}
// Dialogbox für 'doppelt eingetragen'
$("#dialogdoppelt").dialog({
dialogClass: 'no-close',
autoOpen: false,
closeOnEscape: false,
width: 700,
modal: true,
position: 'center',
title: 'Doppelter Eintrag',
open: () => {
showdoppelText();
},
buttons: [
{
text: 'Auswahl übernehmen',
click: async function() {
let gewaehlt = [];
$.each($("input[name='dopp']:checked"), function() {
gewaehlt.push($(this).val());
});
let tln = $("#dialogdoppelt").data('fids').otln;
if (gewaehlt.length === 1) {
if (gewaehlt[0] == "new") {
deleteEntry($("#dialogdoppelt").data('fids').otln.id);
insertEntry();
tln = $("#dialogdoppelt").data('fids').ntln;
}
} else {
insertEntry();
tln = $("#dialogdoppelt").data('fids').ntln;
}
$(this).dialog('close');
let meld = await afterDialogtext(tln);
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
}
}
]
}).prev(".ui-dialog-titlebar").css("background","red");
// Der Eintrag wird jetzt doppelt: dieses melden in einer
// kleinen Dialog-Box
function showDoppelt(oldone, newone) {
$('#dialogdoppelt').data('fids', {otln: oldone, ntln: newone}).dialog('open');
}
// Text für doppelten Eintrag anzeigen
async function showdoppelText() {
const ofdate = await fetchFromDbase({
cmd: 'GET_ONE_DATE',
fid: $("#dialogdoppelt").data('fids').otln.fid
});
const nfdate = await fetchFromDbase({
cmd: 'GET_ONE_DATE',
fid: $("#dialogdoppelt").data('fids').ntln.fid
});
$('#dialogdoppelttext').html('Sie haben sich doppelt eingetragen.<br />' +
'Wenn das so gewollt ist, dann kreuzen Sie bitte unten beide Führungen an,<br />' +
'andernfalls nur diejenige, an der Sie wirklich teilnehmen wollen.<br /> <br />' +
'<input type="checkbox" name="dopp" value="new" checked style="margin-right:10px;">' +
bauDate(nfdate.wtag, nfdate.datum, nfdate.uhrzeit) + '<br />' +
'<input type="checkbox" value="old" name="dopp" style="margin-right:10px;">' +
bauDate(ofdate.wtag, ofdate.datum, ofdate.uhrzeit) + '<br />');
}
const errnum = {'no':1, 'error':2, 'double':3};
// Alle Einträge auf Plausibilität prüfen
// Return:
// Object mit:
// error: 'true', 'false'
// action: 'store', 'exit', 'none'
async function checkPlausibilität() {
let error = false;
// Zuerst prüfen, ob auch alle notwendigen Felder ausgefüllt sind
for (let feld in aktualTln) {
aktualTln[feld] = $('#' + feld).val(); // Feld des actualTln auf dem Wert setzen
if (!mandatory[feld]) {
continue;
}
if (aktualTln[feld] == "") { // Falls das Feld leer ist, den Rahmen
$('#' + feld).css('border-color', 'red'); // rot machen und das
error = true; // errorfralg setzen
}
}
if (error) { // wenn Error, dieses melden
errtext = "Bitte korrigieren Sie die fehlenden Einträge in den rot umrandeten Feldern";
return errnum.error; // und raus
}
// Als Nächstes die PLZ prüfen: Das müssen 5 Ziffern sein
const plz = $('#plz').val();
let re = RegExp(/^[0-9]{5}$/);
if(!re.test(plz)) {
$('#plz').css('border-color', 'red');
errtext = "Bitte geben Sie bei der Postleitzahl mindestens fünf Ziffern und keine Buchstaben ein";
return errnum.error; // und raus
}
// Die Email prüfen
re = RegExp(/^[a-z0-9]+([-_\.]?[a-z0-9])+@[a-z0-9]+([-_\.]?[a-z0-9])+\.[a-z]{2,4}/);
let mail = $('#email').val().toLowerCase();
if(!re.test(mail)) {
$('#email').css('border-color', 'red');
errtext = "Bitte geben Sie eine gültige E-Mail-Adresse ein";
return errnum.error; // und raus
}
// Nun noch doppelte Einträge checken
// dazu prüfen, ob die email_Adresse in den Teilnehmern schon enthalten ist
const aktMail = aktualTln.email;
let allParticipants = await fetchAlleTln();
const find = allParticipants.findIndex((f) => f.email === aktMail);
if (find == -1) {
insertEntry();
return errnum.no;
}
let oldTln = allParticipants[find];
// Email enthalten. Ist der Führungstag identisch? Wenn ja, dann einfach die neuen
// Werte eintragen (also überschreiben)
if (aktualTln.fid === oldTln.fid) {
updateEntry(oldTln.id);
return errnum.no;
} else {
// Hier nun 'Doppelt' melden
showDoppelt(oldTln, aktualTln);
return errnum.double;
}
return errnum.no;
}
// Text für den Dialog der Anmeldung
const afterDialogtext = async(tln) => {
let fdate = await getfdateformatted(tln.fid);
return '<p>Vielen Dank für Ihre Anmeldung. <br /><br />' +
'Wir freuen uns über Ihren Besuch auf der Sternwarte Welzheim am<br /> ' +
'<div id="antwort_fdatum" style="text-align:center;">' +
`${fdate} mit ${tln.anzahl} ${tln.anzahl == 1 ? "Person" : "Personen"}` +
'</div> <br />' +
'Wir haben Ihnen die Anmelde-Bestätigung per Email zugesandt.</p>' +
'<p><a class="button" href="/index.php">Zurück</a></p>';
}
// Formular ist ausgefüllt - der Anmelde-Knopf wurde gerdückt.
// Alle Eingaben in den aktuelTln eintragen und dann
// eine Plausibilitäts-Prüfung durchführen und wenn Alles
// OK ist, die Einträge in die Datenbank schreiben
$('input[name="submit"]').click(async () => {
$('#errordiv').css('visibility','hidden'); // erst mal die evtl. vorhandenen Fehlermeldung
for ( let f in aktualTln) { // löschen und alle Rahmen
$('#'+f).css('border-color','black'); // wieder schwarz machen
}
let nr = $('#fid').find(':selected');
aktualTln.fid = nr[0].value; // selektiertes Führungsdatum merken
const ret = await checkPlausibilität();
if (ret === errnum.error) {
$('#errordiv').html(errtext);
$('#errordiv').css('visibility', 'visible');
} else if (ret === errnum.no) {
let meld = await afterDialogtext(aktualTln);
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
}
});
// Storno
// Der Link für die Stornierung wurde aufgerufen:
// Zuerst die email-Adresse suchen (mit Hilfe des übergebenen md5-hash),
// dann die Stornierung anzeigen und dann noch eine Bestätigung senden
const doStorno = async(md5hash) => {
let emails = await fetchFromDbase({cmd:'GET_ALL_EMAILS'});
for (let em of emails) {
if(md5((em.email+em.fid)) == md5hash) {
// komplette Info dieses teilnehmers holen
let teilnehmer = await fetchFromDbase({cmd: 'GET_TEILN', id: em.id, isid: true});
aktualTln = Object.assign({}, teilnehmer[0]);
// Löschen des Eintrages
let ret = putToDbase({cmd:'DELETE_TLN', id: em.id});
// Meldung anzeigen
let fdate = await getfdateformatted(em.fid);
let meld = '<p><h1 style="text-align:center">Stornierung</h1><br />' +
'Ihre Anmeldung für die Führung auf der Sternwarte Welzheim am<br /><br />' +
'<div id="antwort_fdatum" style="text-align:center;">' +
`${fdate} mit ${aktualTln.anzahl} ${aktualTln.anzahl == 1 ? "Person" : "Personen"}` +
'</div> <br />' +
'wurde storniert.<br /><br /> Wir haben Ihnen die Stornierungs-Bestätigung per Email zugesandt.</p>' +
'<p><a class="button" href="/index.php">Zurück</a></p>';
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
sendConfirmation(true);
return;
}
}
}
// Alle eingetragenen Teilnehmer ab 'morgen' in das globale Array einlesen
const fetchAlleTln = async () => {
const nxtDate = await fetchFromDbase({cmd: 'GET_DATES', anzahl: 1, fid:0});
return await fetchFromDbase({cmd: 'GET_ALLTEILN', fid: nxtDate[0].fid});
}
async function main() {
// Falls Storno angefordert, dann anzeigen
if(md5Hash != "") {
await doStorno(md5Hash);
return;
}
// Alle eingetragenen Teilnehmer ab 'morgen' in das globale Array einlesen
// die nächste 30 Führungsdaten anzeigen
buildfuehrungdates(30);
}
main().catch(console.err);
});

File diff suppressed because one or more lines are too long

View File

@@ -1,40 +0,0 @@
// Javascript-Part für index.php (Home-Page)
(async function() {
const url = 'DB4js.php';
console.log("komme in das Script");
// Von der Datenbank Werte abholen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const fetchFromDbase = async (body) => {
const response = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
let maintenance = await fetchFromDbase({cmd: 'GET_MAINT'});
let dialogMaint = $('#maint').dialog({
autoOpen: false,
open: function () {
$(this).load('maintenance.html', function () {
});
},
width: 700,
height: 200,
modal: true,
resizable: false,
position: {my: 'center top+30%', at: 'center top+20%'},
});
if (maintenance == true) {
dialogMaint.dialog('open');
}
})();

View File

@@ -1,184 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sternwarte Welzheim</title>
<!--[if IE 5]>
<link href="css/ie5.css" rel="stylesheet" type="text/css" />
<![endif]--><!--[if IE]>
<style type="text/css">
<link href="css/ie.css" rel="stylesheet" type="text/css" />
</style>
<![endif]-->
<link href="css/sternwarte1.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/javascript/jquery.js"></script>
<link rel="stylesheet" href="/css/prettyPhoto.css" type="text/css" media="screen" charset="utf-8" />
<script src="/javascript/jquery.prettyPhoto.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
$("a[rel^='prettyPhoto']").prettyPhoto({
animationSpeed: 'normal', /* fast/slow/normal */
padding: 20, /* padding for each side of the picture */
opacity: 1, /* Value betwee 0 and 1 */
showTitle: false, /* true/false */
allowresize: true, /* true/false */
counter_separator_label: '/', /* The separator for the gallery counter 1 "of" 2 */
theme: 'dark_rounded', /* light_rounded / dark_rounded / light_square / dark_square */
callback: function(){}
});
});
</script>
</head>
<body class="thrColFixHdr">
<div id="container">
<?php include 'header.php'; ?>
<?php include 'navi.php'; ?>
<div id="sidebar2">
<?php include 'fdatum.php'; ?>
<?php include 'himmelerg.php'; ?>
</div>
<div id="mainContent">
<h1>Willkommen auf der Seite der Sternwarte Welzheim!</h1>
<p><img src="bilder/sternwarte_winter1.jpg" width="320" height="209" alt="Sternwarte Welzheim " class="fltlft" />Die Beobachtung des Sternenhimmels ist von unseren licht&uuml;berfluteten Städten aus kaum mehr m&ouml;glich. Die Staub und Dunstglocken und der Lichtstrom k&uuml;nstlicher Beleuchtung haben in den letzten Jahren stark zugenommen. </p>
<p> Bedingt durch diese Umweltemissionen ist die Aufhellung des Nachthimmels enorm angestiegen und beeintr&auml;chtigt astronomische Beobachtungen erheblich.<br />
<br />
<a href="sternwarte.php" class="textklein90">weiter
...</a><br />
<br />
</p>
<hr />
<!-- <h1><strong>Venustransit am 5./6. Juni 2012 -</strong> Sonderführung auf der Sternwarte Welzheim</h1>
<p><img src="bilder/aktuell/venustransit2012.jpg" width="320" height="320" class="fltlft" />In der Nacht vom 5. auf 6. Juni 2012 findet das seltene Himmelsschauspiel eines Venustransits statt. Unser Nachbarplanet überholt die Erde auf der Innenbahn. </p>
<p>Zur Beobachtung dieses seltenen Ereignisses bietet die Sternwarte Welzheim am Mittwoch,
6. Juni 2012 eine Sonderführung an. Interessierte Beobachter treffen sich um 5:20 Uhr am Parkplatz der Sternwarte. </p>
<p><a href='venustransit.php' class="textklein90">mehr</a><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<hr />-->
<h1>Allsky- und Wettercam an der Sternwarte Welzheim</h1>
<p><a href='http://www.gfpw.org/skycam/allsky.jpg' rel="prettyPhoto[sky]" title="Aktuelles Bild Allskycam an der Sternwarte Welzheim"><img src="http://www.gfpw.org/skycam/skythumb.jpg" alt="Allskycam Sternwarte Welzheim" width="320" height="240" class="fltlft" /></a>Sie sind sich nicht sicher, ob eine Führung in Welzheim stattfinden wird, weil bei Ihnen zu Hause der Himmel noch bedeckt ist. Mit unserer Allsky- Wettercam können Sie feststellen wie die Wolkenlage an der Sternwarte ist. <br />
<br />
<a href='http://www.gfpw.org/skycam/allsky.jpg' rel="prettyPhoto[pw]" title="Aktuelles Bild Allskycam an der Sternwarte Welzheim" class="textklein90">zur aktuellen Großansicht der Bildes</a></p>
<p>&nbsp;</p>
<p><br />
<br />
<br />
<br />
<br />
</p>
<!-- <hr />
<h1>Partielle Sonnenfinsternis am 4. Januar</h1>
<img src="bilder/sofi201101/sofi_start.jpg" alt="Sternwarte Welzheim im Winter" width="320" height="200" class="fltlft" />Gleich zu Beginn des neuen Jahres bot uns der Himmel ein spannendes Ereignis. Am Dienstag, 4. Januar 2011 fand eine partielle Sonnenfinsternis statt, die von Deutschland aus fast in ihrer gesamten Länge in den Morgenstunden zu verfolgen war. <br />
<br />
<a href="sofi2011.php" class="textklein90"> weitere Bilder der Sonnenfinsternis
..</a>--><br class="clear" />
<p>&nbsp;</p>
<!-- end #mainContent --></div>
<!-- Dieses clear-Element sollte direkt auf das #mainContent-div folgen, um das #container-div anzuweisen, alle untergeordneten Floats aufzunehmen. -->
<br class="clearfloat" />
<!-- end #container -->
</div>
</body>
</html>

View File

@@ -1,9 +0,0 @@
$(document).ready(function () {
$("a[rel^='prettyPhoto']").prettyPhoto({
animationSpeed: 'normal', /* fast/slow/normal */
opacity: 1, /* Value betwee 0 and 1 */// show_title: true, /* true/false */
allow_resize: false, /* true/false */
theme: 'dark_square', /* light_rounded / dark_rounded / light_square / dark_square */
social_tools:''
});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
jquery-1.11.0.min.js

File diff suppressed because one or more lines are too long

View File

@@ -1,562 +0,0 @@
/* ------------------------------------------------------------------------
Class: prettyPhoto
Use: Lightbox clone for jQuery
Author: Stephane Caron (http://www.no-margin-for-errors.com)
Version: 2.4.2
------------------------------------------------------------------------- */
var $pp_pic_holder;
var $ppt;
(function($) {
$.fn.prettyPhoto = function(settings) {
// global Variables
var doresize = true;
var percentBased = false;
var imagesArray = [];
var setPosition = 0; /* Position in the set */
var pp_contentHeight;
var pp_contentWidth;
var pp_containerHeight;
var pp_containerWidth;
var pp_type = 'image';
// Global elements
var $caller;
var $scrollPos = _getScroll();
// Fallback to a supported theme for IE6
if($.browser.msie && $.browser.version == 6 && (settings.theme == 'light_rounded' || settings.theme == 'dark_rounded' || settings.theme == 'dark_square')){
settings.theme = "light_square";
}
$(window).scroll(function(){ $scrollPos = _getScroll(); _centerPicture(); });
$(window).resize(function(){ _centerPicture(); _resizeOverlay(); });
$(document).keypress(function(e){
switch(e.keyCode){
case 37:
if (setPosition == 1) return;
changePicture('previous');
break;
case 39:
if (setPosition == setCount) return;
changePicture('next');
break;
case 27:
close();
break;
};
});
settings = jQuery.extend({
animationSpeed: 'normal', /* fast/slow/normal */
padding: 20, /* padding for each side of the picture */
opacity: 0, /* Value between 0 and 1 */
showTitle: false, /* true/false */
allowresize: true, /* true/false */
counter_separator_label: '/', /* The separator for the gallery counter 1 "of" 2 */
theme: 'dark_square', /* light_rounded / dark_rounded / light_square / dark_square */
callback: function(){}
}, settings);
$(this).each(function(){
var hasTitle = false;
var isSet = false;
var setCount = 0; /* Total images in the set */
var arrayPosition = 0; /* Total position in the array */
imagesArray[imagesArray.length] = this;
$(this).bind('click',function(){
open(this);
return false;
});
});
function open(el) {
$caller = $(el);
// Find out if the picture is part of a set
theRel = $caller.attr('rel');
galleryRegExp = /\[(?:.*)\]/;
theGallery = galleryRegExp.exec(theRel);
// Calculate the number of items in the set, and the position of the clicked picture.
isSet = false;
setCount = 0;
_getFileType();
for (i = 0; i < imagesArray.length; i++){
if($(imagesArray[i]).attr('rel').indexOf(theGallery) != -1){
setCount++;
if(setCount > 1) isSet = true;
if($(imagesArray[i]).attr('href') == $caller.attr('href')){
setPosition = setCount;
arrayPosition = i;
};
};
};
_buildOverlay();
// Display the current position
$pp_pic_holder.find('p.currentTextHolder').text(setPosition + settings.counter_separator_label + setCount);
// Position the picture in the center of the viewing area
_centerPicture();
$('#pp_full_res').hide();
$pp_pic_holder.find('.pp_loaderIcon').show();
};
showimage = function(width,height,containerWidth,containerHeight,contentHeight,contentWidth,resized){
$('.pp_loaderIcon').hide();
if($.browser.opera) {
windowHeight = window.innerHeight;
windowWidth = window.innerWidth;
}else{
windowHeight = $(window).height();
windowWidth = $(window).width();
};
$pp_pic_holder.find('.pp_content').animate({'height':contentHeight},settings.animationSpeed);
projectedTop = $scrollPos['scrollTop'] + ((windowHeight/2) - (containerHeight/2));
if(projectedTop < 0) projectedTop = 0 + $pp_pic_holder.find('.ppt').height();
// Resize the holder
$pp_pic_holder.animate({
'top': projectedTop,
'left': ((windowWidth/2) - (containerWidth/2)),
'width': containerWidth
},settings.animationSpeed,function(){
$pp_pic_holder.width(containerWidth);
$pp_pic_holder.find('.pp_hoverContainer,#fullResImage').height(height).width(width);
// Fade the new image
$pp_pic_holder.find('#pp_full_res').fadeIn(settings.animationSpeed,function(){
$(this).find('object,embed').css('visibility','visible');
});
// Show the nav elements
_showContent();
// Fade the resizing link if the image is resized
if(resized) $('a.pp_expand,a.pp_contract').fadeIn(settings.animationSpeed);
});
};
function _showContent(){
// Show the nav
if(isSet && pp_type=="image") { $pp_pic_holder.find('.pp_hoverContainer').fadeIn(settings.animationSpeed); }else{ $pp_pic_holder.find('.pp_hoverContainer').hide(); }
$pp_pic_holder.find('.pp_details').fadeIn(settings.animationSpeed);
// Show the title
if(settings.showTitle && hasTitle){
$ppt.css({
'top' : $pp_pic_holder.offset().top - 22,
'left' : $pp_pic_holder.offset().left + (settings.padding/2),
'display' : 'none'
});
$ppt.fadeIn(settings.animationSpeed);
};
}
function _hideContent(){
// Fade out the current picture
$pp_pic_holder.find('.pp_hoverContainer,.pp_details').fadeOut(settings.animationSpeed);
$pp_pic_holder.find('#pp_full_res object,#pp_full_res embed').css('visibility','hidden');
$pp_pic_holder.find('#pp_full_res').fadeOut(settings.animationSpeed,function(){
$('.pp_loaderIcon').show();
// Preload the image
_preload();
});
// Hide the title
$ppt.fadeOut(settings.animationSpeed);
}
function changePicture(direction){
if(direction == 'previous') {
arrayPosition--;
setPosition--;
}else{
arrayPosition++;
setPosition++;
};
// Allow the resizing of the images
if(!doresize) doresize = true;
_hideContent();
$('a.pp_expand,a.pp_contract').fadeOut(settings.animationSpeed,function(){
$(this).removeClass('pp_contract').addClass('pp_expand');
});
};
function close(){
$pp_pic_holder.find('object,embed').css('visibility','hidden');
$('div.pp_pic_holder,div.ppt').fadeOut(settings.animationSpeed);
$('div.pp_overlay').fadeOut(settings.animationSpeed, function(){
$('div.pp_overlay,div.pp_pic_holder,div.ppt').remove();
// To fix the bug with IE select boxes
if($.browser.msie && $.browser.version == 6){
$('select').css('visibility','visible');
};
settings.callback();
});
doresize = true;
};
function _checkPosition(){
// If at the end, hide the next link
if(setPosition == setCount) {
$pp_pic_holder.find('a.pp_next').css('visibility','hidden');
$pp_pic_holder.find('a.pp_arrow_next').addClass('disabled').unbind('click');
}else{
$pp_pic_holder.find('a.pp_next').css('visibility','visible');
$pp_pic_holder.find('a.pp_arrow_next.disabled').removeClass('disabled').bind('click',function(){
changePicture('next');
return false;
});
};
// If at the beginning, hide the previous link
if(setPosition == 1) {
$pp_pic_holder.find('a.pp_previous').css('visibility','hidden');
$pp_pic_holder.find('a.pp_arrow_previous').addClass('disabled').unbind('click');
}else{
$pp_pic_holder.find('a.pp_previous').css('visibility','visible');
$pp_pic_holder.find('a.pp_arrow_previous.disabled').removeClass('disabled').bind('click',function(){
changePicture('previous');
return false;
});
};
// Change the current picture text
$pp_pic_holder.find('p.currentTextHolder').text(setPosition + settings.counter_separator_label + setCount);
$caller = (isSet) ? $(imagesArray[arrayPosition]) : $caller;
_getFileType();
if($caller.attr('title')){
$pp_pic_holder.find('.pp_description').show().html(unescape($caller.attr('title')));
}else{
$pp_pic_holder.find('.pp_description').hide().text('');
};
if($caller.find('img').attr('alt') && settings.showTitle){
hasTitle = true;
$ppt.html(unescape($caller.find('img').attr('alt')));
}else{
hasTitle = false;
};
};
function _fitToViewport(width,height){
hasBeenResized = false;
_getDimensions(width,height);
// Define them in case there's no resize needed
imageWidth = width;
imageHeight = height;
windowHeight = $(window).height();
windowWidth = $(window).width();
if( ((pp_containerWidth > windowWidth) || (pp_containerHeight > windowHeight)) && doresize && settings.allowresize && !percentBased) {
hasBeenResized = true;
notFitting = true;
while (notFitting){
if((pp_containerWidth > windowWidth)){
imageWidth = (windowWidth - 200);
imageHeight = (height/width) * imageWidth;
}else if((pp_containerHeight > windowHeight)){
imageHeight = (windowHeight - 200);
imageWidth = (width/height) * imageHeight;
}else{
notFitting = false;
};
pp_containerHeight = imageHeight;
pp_containerWidth = imageWidth;
};
_getDimensions(imageWidth,imageHeight);
};
return {
width:imageWidth,
height:imageHeight,
containerHeight:pp_containerHeight,
containerWidth:pp_containerWidth,
contentHeight:pp_contentHeight,
contentWidth:pp_contentWidth,
resized:hasBeenResized
};
};
function _getDimensions(width,height){
$pp_pic_holder.find('.pp_details').width(width).find('.pp_description').width(width - parseFloat($pp_pic_holder.find('a.pp_close').css('width'))); /* To have the correct height */
// Get the container size, to resize the holder to the right dimensions
pp_contentHeight = height + $pp_pic_holder.find('.pp_details').height() + parseFloat($pp_pic_holder.find('.pp_details').css('marginTop')) + parseFloat($pp_pic_holder.find('.pp_details').css('marginBottom'));
pp_contentWidth = width;
pp_containerHeight = pp_contentHeight + $pp_pic_holder.find('.ppt').height() + $pp_pic_holder.find('.pp_top').height() + $pp_pic_holder.find('.pp_bottom').height();
pp_containerWidth = width + settings.padding;
}
function _getFileType(){
if ($caller.attr('href').match(/youtube\.com\/watch/i)) {
pp_type = 'youtube';
}else if($caller.attr('href').indexOf('.mov') != -1){
pp_type = 'quicktime';
}else if($caller.attr('href').indexOf('.swf') != -1){
pp_type = 'flash';
}else if($caller.attr('href').indexOf('iframe') != -1){
pp_type = 'iframe'
}else{
pp_type = 'image';
}
}
function _centerPicture(){
if ($pp_pic_holder){ if($pp_pic_holder.size() == 0){ return; }}else{ return; }; //Make sure the gallery is open
if($.browser.opera) {
windowHeight = window.innerHeight;
windowWidth = window.innerWidth;
}else{
windowHeight = $(window).height();
windowWidth = $(window).width();
};
if(doresize) {
$pHeight = $pp_pic_holder.height();
$pWidth = $pp_pic_holder.width();
$tHeight = $ppt.height();
projectedTop = (windowHeight/2) + $scrollPos['scrollTop'] - ($pHeight/2);
if(projectedTop < 0) projectedTop = 0 + $tHeight;
$pp_pic_holder.css({
'top': projectedTop,
'left': (windowWidth/2) + $scrollPos['scrollLeft'] - ($pWidth/2)
});
$ppt.css({
'top' : projectedTop - $tHeight,
'left' : (windowWidth/2) + $scrollPos['scrollLeft'] - ($pWidth/2) + (settings.padding/2)
});
};
};
function _preload(){
// Hide the next/previous links if on first or last images.
_checkPosition();
if(pp_type == 'image'){
// Set the new image
imgPreloader = new Image();
// Preload the neighbour images
nextImage = new Image();
if(isSet && setPosition > setCount) nextImage.src = $(imagesArray[arrayPosition + 1]).attr('href');
prevImage = new Image();
if(isSet && imagesArray[arrayPosition - 1]) prevImage.src = $(imagesArray[arrayPosition - 1]).attr('href');
pp_typeMarkup = '<img id="fullResImage" src="" />';
$pp_pic_holder.find('#pp_full_res')[0].innerHTML = pp_typeMarkup;
$pp_pic_holder.find('.pp_content').css('overflow','hidden');
$pp_pic_holder.find('#fullResImage').attr('src',$caller.attr('href'));
imgPreloader.onload = function(){
var correctSizes = _fitToViewport(imgPreloader.width,imgPreloader.height);
imgPreloader.width = correctSizes['width'];
imgPreloader.height = correctSizes['height'];
showimage(imgPreloader.width,imgPreloader.height,correctSizes["containerWidth"],correctSizes["containerHeight"],correctSizes["contentHeight"],correctSizes["contentWidth"],correctSizes["resized"]);
};
imgPreloader.src = $caller.attr('href');
}else{
// Get the dimensions
movie_width = ( parseFloat(grab_param('width',$caller.attr('href'))) ) ? grab_param('width',$caller.attr('href')) : 425;
movie_height = ( parseFloat(grab_param('height',$caller.attr('href'))) ) ? grab_param('height',$caller.attr('href')) : 344;
// If the size is % based
if(movie_width.indexOf('%') != -1 || movie_height.indexOf('%') != -1){
movie_height = ($(window).height() * parseFloat(movie_height) / 100) - 100;
movie_width = ($(window).width() * parseFloat(movie_width) / 100) - 100;
parsentBased = true;
}else{
movie_height = parseFloat(movie_height);
movie_width = parseFloat(movie_width);
}
if(pp_type == 'quicktime'){ movie_height+=13; }
// Fit them to viewport
correctSizes = _fitToViewport(movie_width,movie_height);
if(pp_type == 'youtube'){
pp_typeMarkup = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+correctSizes['width']+'" height="'+correctSizes['height']+'"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://www.youtube.com/v/'+grab_param('v',$caller.attr('href'))+'" /><embed src="http://www.youtube.com/v/'+grab_param('v',$caller.attr('href'))+'" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="'+correctSizes['width']+'" height="'+correctSizes['height']+'"></embed></object>';
}else if(pp_type == 'quicktime'){
pp_typeMarkup = '<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" height="'+correctSizes['height']+'" width="'+correctSizes['width']+'"><param name="src" value="'+$caller.attr('href')+'"><param name="autoplay" value="true"><param name="type" value="video/quicktime"><embed src="'+$caller.attr('href')+'" height="'+correctSizes['height']+'" width="'+correctSizes['width']+'" autoplay="true" type="video/quicktime" pluginspage="http://www.apple.com/quicktime/download/"></embed></object>';
}else if(pp_type == 'flash'){
flash_vars = $caller.attr('href');
flash_vars = flash_vars.substring($caller.attr('href').indexOf('flashvars') + 10,$caller.attr('href').length);
filename = $caller.attr('href');
filename = filename.substring(0,filename.indexOf('?'));
pp_typeMarkup = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+correctSizes['width']+'" height="'+correctSizes['height']+'"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="'+filename+'?'+flash_vars+'" /><embed src="'+filename+'?'+flash_vars+'" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="'+correctSizes['width']+'" height="'+correctSizes['height']+'"></embed></object>';
}else if(pp_type == 'iframe'){
movie_url = $caller.attr('href');
movie_url = movie_url.substr(0,movie_url.indexOf('?'));
pp_typeMarkup = '<iframe src ="'+movie_url+'" width="'+(correctSizes['width']-10)+'" height="'+(correctSizes['height']-10)+'" frameborder="no"></iframe>';
}
// Append HTML
$pp_pic_holder.find('#pp_full_res')[0].innerHTML = pp_typeMarkup;
// Show content
showimage(correctSizes['width'],correctSizes['height'],correctSizes["containerWidth"],correctSizes["containerHeight"],correctSizes["contentHeight"],correctSizes["contentWidth"],correctSizes["resized"]);
}
};
function _getScroll(){
if (self.pageYOffset) {
scrollTop = self.pageYOffset;
scrollLeft = self.pageXOffset;
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
scrollTop = document.documentElement.scrollTop;
scrollLeft = document.documentElement.scrollLeft;
} else if (document.body) {// all other Explorers
scrollTop = document.body.scrollTop;
scrollLeft = document.body.scrollLeft;
}
return {scrollTop:scrollTop,scrollLeft:scrollLeft};
};
function _resizeOverlay() {
$('div.pp_overlay').css({
'height':$(document).height(),
'width':$(window).width()
});
};
function _buildOverlay(){
toInject = "";
// Build the background overlay div
toInject += "<div class='pp_overlay'></div>";
// Define the markup to append, depending on the content type.
if(pp_type == 'image'){
pp_typeMarkup = '<img id="fullResImage" src="" />';
}else{
pp_typeMarkup = '';
}
// Basic HTML for the picture holder
toInject += '<div class="pp_pic_holder"><div class="pp_top"><div class="pp_left"></div><div class="pp_middle"></div><div class="pp_right"></div></div><div class="pp_content"><a href="#" class="pp_expand" title="Expand the image">Expand</a><div class="pp_loaderIcon"></div><div class="pp_hoverContainer"><a class="pp_next" href="#">next</a><a class="pp_previous" href="#">previous</a></div><div id="pp_full_res">'+ pp_typeMarkup +'</div><div class="pp_details clearfix"><a class="pp_close" href="#">Close</a><p class="pp_description"></p><div class="pp_nav"><a href="#" class="pp_arrow_previous">Previous</a><p class="currentTextHolder">0'+settings.counter_separator_label+'0</p><a href="#" class="pp_arrow_next">Next</a></div></div></div><div class="pp_bottom"><div class="pp_left"></div><div class="pp_middle"></div><div class="pp_right"></div></div></div>';
// Basic html for the title holder
toInject += '<div class="ppt"></div>';
$('body').append(toInject);
// Set my global selectors
$pp_pic_holder = $('.pp_pic_holder');
$ppt = $('.ppt');
$('div.pp_overlay').css('height',$(document).height()).bind('click',function(){
close();
});
$pp_pic_holder.css({'opacity': 0}).addClass(settings.theme);
$('a.pp_close').bind('click',function(){ close(); return false; });
$('a.pp_expand').bind('click',function(){
$this = $(this);
// Expand the image
if($this.hasClass('pp_expand')){
$this.removeClass('pp_expand').addClass('pp_contract');
doresize = false;
}else{
$this.removeClass('pp_contract').addClass('pp_expand');
doresize = true;
};
_hideContent();
$pp_pic_holder.find('.pp_hoverContainer, #pp_full_res, .pp_details').fadeOut(settings.animationSpeed,function(){
_preload();
});
return false;
});
$pp_pic_holder.find('.pp_previous, .pp_arrow_previous').bind('click',function(){
changePicture('previous');
return false;
});
$pp_pic_holder.find('.pp_next, .pp_arrow_next').bind('click',function(){
changePicture('next');
return false;
});
$pp_pic_holder.find('.pp_hoverContainer').css({
'margin-left': settings.padding/2
});
// If it's not a set, hide the links
if(!isSet) {
$pp_pic_holder.find('.pp_hoverContainer,.pp_nav').hide();
};
// To fix the bug with IE select boxes
if($.browser.msie && $.browser.version == 6){
$('body').addClass('ie6');
$('select').css('visibility','hidden');
};
// Then fade it in
$('div.pp_overlay').css('opacity',0).fadeTo(settings.animationSpeed,settings.opacity, function(){
$pp_pic_holder.css('opacity',0).fadeIn(settings.animationSpeed,function(){
$pp_pic_holder.attr('style','left:'+$pp_pic_holder.css('left')+';top:'+$pp_pic_holder.css('top')+';');
_preload();
});
});
};
};
function grab_param(name,url){
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( url );
if( results == null )
return "";
else
return results[1];
}
})(jQuery);

View File

@@ -1,911 +0,0 @@
/* ------------------------------------------------------------------------
Class: prettyPhoto
Use: Lightbox clone for jQuery
Author: Stephane Caron (http://www.no-margin-for-errors.com)
Version: 3.1.6
------------------------------------------------------------------------- */
(function($) {
$.prettyPhoto = {version: '3.1.6'};
$.fn.prettyPhoto = function(pp_settings) {
pp_settings = jQuery.extend({
hook: 'rel', /* the attribute tag to use for prettyPhoto hooks. default: 'rel'. For HTML5, use "data-rel" or similar. */
animation_speed: 'fast', /* fast/slow/normal */
ajaxcallback: function() {},
slideshow: 5000, /* false OR interval time in ms */
autoplay_slideshow: false, /* true/false */
opacity: 0.80, /* Value between 0 and 1 */
show_title: true, /* true/false */
allow_resize: true, /* Resize the photos bigger than viewport. true/false */
allow_expand: true, /* Allow the user to expand a resized image. true/false */
default_width: 500,
default_height: 344,
counter_separator_label: '/', /* The separator for the gallery counter 1 "of" 2 */
theme: 'pp_default', /* light_rounded / dark_rounded / light_square / dark_square / facebook */
horizontal_padding: 20, /* The padding on each side of the picture */
hideflash: false, /* Hides all the flash object on a page, set to TRUE if flash appears over prettyPhoto */
wmode: 'opaque', /* Set the flash wmode attribute */
autoplay: true, /* Automatically start videos: True/False */
modal: false, /* If set to true, only the close button will close the window */
deeplinking: true, /* Allow prettyPhoto to update the url to enable deeplinking. */
overlay_gallery: true, /* If set to true, a gallery will overlay the fullscreen image on mouse over */
overlay_gallery_max: 30, /* Maximum number of pictures in the overlay gallery */
keyboard_shortcuts: true, /* Set to false if you open forms inside prettyPhoto */
changepicturecallback: function(){}, /* Called everytime an item is shown/changed */
callback: function(){}, /* Called when prettyPhoto is closed */
ie6_fallback: true,
markup: '<div class="pp_pic_holder"> \
<div class="ppt">&nbsp;</div> \
<div class="pp_top"> \
<div class="pp_left"></div> \
<div class="pp_middle"></div> \
<div class="pp_right"></div> \
</div> \
<div class="pp_content_container"> \
<div class="pp_left"> \
<div class="pp_right"> \
<div class="pp_content"> \
<div class="pp_loaderIcon"></div> \
<div class="pp_fade"> \
<a href="#" class="pp_expand" title="Expand the image">Expand</a> \
<div class="pp_hoverContainer"> \
<a class="pp_next" href="#">next</a> \
<a class="pp_previous" href="#">previous</a> \
</div> \
<div id="pp_full_res"></div> \
<div class="pp_details"> \
<div class="pp_nav"> \
<a href="#" class="pp_arrow_previous">Previous</a> \
<p class="currentTextHolder">0/0</p> \
<a href="#" class="pp_arrow_next">Next</a> \
</div> \
<p class="pp_description"></p> \
<div class="pp_social">{pp_social}</div> \
<a class="pp_close" href="#">Close</a> \
</div> \
</div> \
</div> \
</div> \
</div> \
</div> \
<div class="pp_bottom"> \
<div class="pp_left"></div> \
<div class="pp_middle"></div> \
<div class="pp_right"></div> \
</div> \
</div> \
<div class="pp_overlay"></div>',
gallery_markup: '<div class="pp_gallery"> \
<a href="#" class="pp_arrow_previous">Previous</a> \
<div> \
<ul> \
{gallery} \
</ul> \
</div> \
<a href="#" class="pp_arrow_next">Next</a> \
</div>',
image_markup: '<img id="fullResImage" src="{path}" />',
flash_markup: '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{width}" height="{height}"><param name="wmode" value="{wmode}" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="{path}" /><embed src="{path}" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="{width}" height="{height}" wmode="{wmode}"></embed></object>',
quicktime_markup: '<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" height="{height}" width="{width}"><param name="src" value="{path}"><param name="autoplay" value="{autoplay}"><param name="type" value="video/quicktime"><embed src="{path}" height="{height}" width="{width}" autoplay="{autoplay}" type="video/quicktime" pluginspage="http://www.apple.com/quicktime/download/"></embed></object>',
iframe_markup: '<iframe src ="{path}" width="{width}" height="{height}" frameborder="no"></iframe>',
inline_markup: '<div class="pp_inline">{content}</div>',
custom_markup: '',
social_tools: '<div class="twitter"><a href="http://twitter.com/share" class="twitter-share-button" data-count="none">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="facebook"><iframe src="//www.facebook.com/plugins/like.php?locale=en_US&href={location_href}&amp;layout=button_count&amp;show_faces=true&amp;width=500&amp;action=like&amp;font&amp;colorscheme=light&amp;height=23" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:500px; height:23px;" allowTransparency="true"></iframe></div>' /* html or false to disable */
}, pp_settings);
// Global variables accessible only by prettyPhoto
var matchedObjects = this, percentBased = false, pp_dimensions, pp_open,
// prettyPhoto container specific
pp_contentHeight, pp_contentWidth, pp_containerHeight, pp_containerWidth,
// Window size
windowHeight = $(window).height(), windowWidth = $(window).width(),
// Global elements
pp_slideshow;
doresize = true, scroll_pos = _get_scroll();
// Window/Keyboard events
$(window).unbind('resize.prettyphoto').bind('resize.prettyphoto',function(){ _center_overlay(); _resize_overlay(); });
if(pp_settings.keyboard_shortcuts) {
$(document).unbind('keydown.prettyphoto').bind('keydown.prettyphoto',function(e){
if(typeof $pp_pic_holder != 'undefined'){
if($pp_pic_holder.is(':visible')){
switch(e.keyCode){
case 37:
$.prettyPhoto.changePage('previous');
e.preventDefault();
break;
case 39:
$.prettyPhoto.changePage('next');
e.preventDefault();
break;
case 27:
if(!settings.modal)
$.prettyPhoto.close();
e.preventDefault();
break;
};
// return false;
};
};
});
};
/**
* Initialize prettyPhoto.
*/
$.prettyPhoto.initialize = function() {
settings = pp_settings;
if(settings.theme == 'pp_default') settings.horizontal_padding = 16;
// Find out if the picture is part of a set
theRel = $(this).attr(settings.hook);
galleryRegExp = /\[(?:.*)\]/;
isSet = (galleryRegExp.exec(theRel)) ? true : false;
// Put the SRCs, TITLEs, ALTs into an array.
pp_images = (isSet) ? jQuery.map(matchedObjects, function(n, i){ if($(n).attr(settings.hook).indexOf(theRel) != -1) return $(n).attr('href'); }) : $.makeArray($(this).attr('href'));
pp_titles = (isSet) ? jQuery.map(matchedObjects, function(n, i){ if($(n).attr(settings.hook).indexOf(theRel) != -1) return ($(n).find('img').attr('alt')) ? $(n).find('img').attr('alt') : ""; }) : $.makeArray($(this).find('img').attr('alt'));
pp_descriptions = (isSet) ? jQuery.map(matchedObjects, function(n, i){ if($(n).attr(settings.hook).indexOf(theRel) != -1) return ($(n).attr('title')) ? $(n).attr('title') : ""; }) : $.makeArray($(this).attr('title'));
if(pp_images.length > settings.overlay_gallery_max) settings.overlay_gallery = false;
set_position = jQuery.inArray($(this).attr('href'), pp_images); // Define where in the array the clicked item is positionned
rel_index = (isSet) ? set_position : $("a["+settings.hook+"^='"+theRel+"']").index($(this));
_build_overlay(this); // Build the overlay {this} being the caller
if(settings.allow_resize)
$(window).bind('scroll.prettyphoto',function(){ _center_overlay(); });
$.prettyPhoto.open();
return false;
}
/**
* Opens the prettyPhoto modal box.
* @param image {String,Array} Full path to the image to be open, can also be an array containing full images paths.
* @param title {String,Array} The title to be displayed with the picture, can also be an array containing all the titles.
* @param description {String,Array} The description to be displayed with the picture, can also be an array containing all the descriptions.
*/
$.prettyPhoto.open = function(event) {
if(typeof settings == "undefined"){ // Means it's an API call, need to manually get the settings and set the variables
settings = pp_settings;
pp_images = $.makeArray(arguments[0]);
pp_titles = (arguments[1]) ? $.makeArray(arguments[1]) : $.makeArray("");
pp_descriptions = (arguments[2]) ? $.makeArray(arguments[2]) : $.makeArray("");
isSet = (pp_images.length > 1) ? true : false;
set_position = (arguments[3])? arguments[3]: 0;
_build_overlay(event.target); // Build the overlay {this} being the caller
}
if(settings.hideflash) $('object,embed,iframe[src*=youtube],iframe[src*=vimeo]').css('visibility','hidden'); // Hide the flash
_checkPosition($(pp_images).size()); // Hide the next/previous links if on first or last images.
$('.pp_loaderIcon').show();
if(settings.deeplinking)
setHashtag();
// Rebuild Facebook Like Button with updated href
if(settings.social_tools){
facebook_like_link = settings.social_tools.replace('{location_href}', encodeURIComponent(location.href));
$pp_pic_holder.find('.pp_social').html(facebook_like_link);
}
// Fade the content in
if($ppt.is(':hidden')) $ppt.css('opacity',0).show();
$pp_overlay.show().fadeTo(settings.animation_speed,settings.opacity);
// Display the current position
$pp_pic_holder.find('.currentTextHolder').text((set_position+1) + settings.counter_separator_label + $(pp_images).size());
// Set the description
if(typeof pp_descriptions[set_position] != 'undefined' && pp_descriptions[set_position] != ""){
$pp_pic_holder.find('.pp_description').show().html(unescape(pp_descriptions[set_position]));
}else{
$pp_pic_holder.find('.pp_description').hide();
}
// Get the dimensions
movie_width = ( parseFloat(getParam('width',pp_images[set_position])) ) ? getParam('width',pp_images[set_position]) : settings.default_width.toString();
movie_height = ( parseFloat(getParam('height',pp_images[set_position])) ) ? getParam('height',pp_images[set_position]) : settings.default_height.toString();
// If the size is % based, calculate according to window dimensions
percentBased=false;
if(movie_height.indexOf('%') != -1) { movie_height = parseFloat(($(window).height() * parseFloat(movie_height) / 100) - 150); percentBased = true; }
if(movie_width.indexOf('%') != -1) { movie_width = parseFloat(($(window).width() * parseFloat(movie_width) / 100) - 150); percentBased = true; }
// Fade the holder
$pp_pic_holder.fadeIn(function(){
// Set the title
(settings.show_title && pp_titles[set_position] != "" && typeof pp_titles[set_position] != "undefined") ? $ppt.html(unescape(pp_titles[set_position])) : $ppt.html('&nbsp;');
imgPreloader = "";
skipInjection = false;
// Inject the proper content
switch(_getFileType(pp_images[set_position])){
case 'image':
imgPreloader = new Image();
// Preload the neighbour images
nextImage = new Image();
if(isSet && set_position < $(pp_images).size() -1) nextImage.src = pp_images[set_position + 1];
prevImage = new Image();
if(isSet && pp_images[set_position - 1]) prevImage.src = pp_images[set_position - 1];
$pp_pic_holder.find('#pp_full_res')[0].innerHTML = settings.image_markup.replace(/{path}/g,pp_images[set_position]);
imgPreloader.onload = function(){
// Fit item to viewport
pp_dimensions = _fitToViewport(imgPreloader.width,imgPreloader.height);
_showContent();
};
imgPreloader.onerror = function(){
alert('Image cannot be loaded. Make sure the path is correct and image exist.');
$.prettyPhoto.close();
};
imgPreloader.src = pp_images[set_position];
break;
case 'youtube':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
// Regular youtube link
movie_id = getParam('v',pp_images[set_position]);
// youtu.be link
if(movie_id == ""){
movie_id = pp_images[set_position].split('youtu.be/');
movie_id = movie_id[1];
if(movie_id.indexOf('?') > 0)
movie_id = movie_id.substr(0,movie_id.indexOf('?')); // Strip anything after the ?
if(movie_id.indexOf('&') > 0)
movie_id = movie_id.substr(0,movie_id.indexOf('&')); // Strip anything after the &
}
movie = 'http://www.youtube.com/embed/'+movie_id;
(getParam('rel',pp_images[set_position])) ? movie+="?rel="+getParam('rel',pp_images[set_position]) : movie+="?rel=1";
if(settings.autoplay) movie += "&autoplay=1";
toInject = settings.iframe_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,movie);
break;
case 'vimeo':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
movie_id = pp_images[set_position];
var regExp = /http(s?):\/\/(www\.)?vimeo.com\/(\d+)/;
var match = movie_id.match(regExp);
movie = 'http://player.vimeo.com/video/'+ match[3] +'?title=0&amp;byline=0&amp;portrait=0';
if(settings.autoplay) movie += "&autoplay=1;";
vimeo_width = pp_dimensions['width'] + '/embed/?moog_width='+ pp_dimensions['width'];
toInject = settings.iframe_markup.replace(/{width}/g,vimeo_width).replace(/{height}/g,pp_dimensions['height']).replace(/{path}/g,movie);
break;
case 'quicktime':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
pp_dimensions['height']+=15; pp_dimensions['contentHeight']+=15; pp_dimensions['containerHeight']+=15; // Add space for the control bar
toInject = settings.quicktime_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,pp_images[set_position]).replace(/{autoplay}/g,settings.autoplay);
break;
case 'flash':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
flash_vars = pp_images[set_position];
flash_vars = flash_vars.substring(pp_images[set_position].indexOf('flashvars') + 10,pp_images[set_position].length);
filename = pp_images[set_position];
filename = filename.substring(0,filename.indexOf('?'));
toInject = settings.flash_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,filename+'?'+flash_vars);
break;
case 'iframe':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
frame_url = pp_images[set_position];
frame_url = frame_url.substr(0,frame_url.indexOf('iframe')-1);
toInject = settings.iframe_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{path}/g,frame_url);
break;
case 'ajax':
doresize = false; // Make sure the dimensions are not resized.
pp_dimensions = _fitToViewport(movie_width,movie_height);
doresize = true; // Reset the dimensions
skipInjection = true;
$.get(pp_images[set_position],function(responseHTML){
toInject = settings.inline_markup.replace(/{content}/g,responseHTML);
$pp_pic_holder.find('#pp_full_res')[0].innerHTML = toInject;
_showContent();
});
break;
case 'custom':
pp_dimensions = _fitToViewport(movie_width,movie_height); // Fit item to viewport
toInject = settings.custom_markup;
break;
case 'inline':
// to get the item height clone it, apply default width, wrap it in the prettyPhoto containers , then delete
myClone = $(pp_images[set_position]).clone().append('<br clear="all" />').css({'width':settings.default_width}).wrapInner('<div id="pp_full_res"><div class="pp_inline"></div></div>').appendTo($('body')).show();
doresize = false; // Make sure the dimensions are not resized.
pp_dimensions = _fitToViewport($(myClone).width(),$(myClone).height());
doresize = true; // Reset the dimensions
$(myClone).remove();
toInject = settings.inline_markup.replace(/{content}/g,$(pp_images[set_position]).html());
break;
};
if(!imgPreloader && !skipInjection){
$pp_pic_holder.find('#pp_full_res')[0].innerHTML = toInject;
// Show content
_showContent();
};
});
return false;
};
/**
* Change page in the prettyPhoto modal box
* @param direction {String} Direction of the paging, previous or next.
*/
$.prettyPhoto.changePage = function(direction){
currentGalleryPage = 0;
if(direction == 'previous') {
set_position--;
if (set_position < 0) set_position = $(pp_images).size()-1;
}else if(direction == 'next'){
set_position++;
if(set_position > $(pp_images).size()-1) set_position = 0;
}else{
set_position=direction;
};
rel_index = set_position;
if(!doresize) doresize = true; // Allow the resizing of the images
if(settings.allow_expand) {
$('.pp_contract').removeClass('pp_contract').addClass('pp_expand');
}
_hideContent(function(){ $.prettyPhoto.open(); });
};
/**
* Change gallery page in the prettyPhoto modal box
* @param direction {String} Direction of the paging, previous or next.
*/
$.prettyPhoto.changeGalleryPage = function(direction){
if(direction=='next'){
currentGalleryPage ++;
if(currentGalleryPage > totalPage) currentGalleryPage = 0;
}else if(direction=='previous'){
currentGalleryPage --;
if(currentGalleryPage < 0) currentGalleryPage = totalPage;
}else{
currentGalleryPage = direction;
};
slide_speed = (direction == 'next' || direction == 'previous') ? settings.animation_speed : 0;
slide_to = currentGalleryPage * (itemsPerPage * itemWidth);
$pp_gallery.find('ul').animate({left:-slide_to},slide_speed);
};
/**
* Start the slideshow...
*/
$.prettyPhoto.startSlideshow = function(){
if(typeof pp_slideshow == 'undefined'){
$pp_pic_holder.find('.pp_play').unbind('click').removeClass('pp_play').addClass('pp_pause').click(function(){
$.prettyPhoto.stopSlideshow();
return false;
});
pp_slideshow = setInterval($.prettyPhoto.startSlideshow,settings.slideshow);
}else{
$.prettyPhoto.changePage('next');
};
}
/**
* Stop the slideshow...
*/
$.prettyPhoto.stopSlideshow = function(){
$pp_pic_holder.find('.pp_pause').unbind('click').removeClass('pp_pause').addClass('pp_play').click(function(){
$.prettyPhoto.startSlideshow();
return false;
});
clearInterval(pp_slideshow);
pp_slideshow=undefined;
}
/**
* Closes prettyPhoto.
*/
$.prettyPhoto.close = function(){
if($pp_overlay.is(":animated")) return;
$.prettyPhoto.stopSlideshow();
$pp_pic_holder.stop().find('object,embed').css('visibility','hidden');
$('div.pp_pic_holder,div.ppt,.pp_fade').fadeOut(settings.animation_speed,function(){ $(this).remove(); });
$pp_overlay.fadeOut(settings.animation_speed, function(){
if(settings.hideflash) $('object,embed,iframe[src*=youtube],iframe[src*=vimeo]').css('visibility','visible'); // Show the flash
$(this).remove(); // No more need for the prettyPhoto markup
$(window).unbind('scroll.prettyphoto');
clearHashtag();
settings.callback();
doresize = true;
pp_open = false;
delete settings;
});
};
/**
* Set the proper sizes on the containers and animate the content in.
*/
function _showContent(){
$('.pp_loaderIcon').hide();
// Calculate the opened top position of the pic holder
projectedTop = scroll_pos['scrollTop'] + ((windowHeight/2) - (pp_dimensions['containerHeight']/2));
if(projectedTop < 0) projectedTop = 0;
$ppt.fadeTo(settings.animation_speed,1);
// Resize the content holder
$pp_pic_holder.find('.pp_content')
.animate({
height:pp_dimensions['contentHeight'],
width:pp_dimensions['contentWidth']
},settings.animation_speed);
// Resize picture the holder
$pp_pic_holder.animate({
'top': projectedTop,
'left': ((windowWidth/2) - (pp_dimensions['containerWidth']/2) < 0) ? 0 : (windowWidth/2) - (pp_dimensions['containerWidth']/2),
width:pp_dimensions['containerWidth']
},settings.animation_speed,function(){
$pp_pic_holder.find('.pp_hoverContainer,#fullResImage').height(pp_dimensions['height']).width(pp_dimensions['width']);
$pp_pic_holder.find('.pp_fade').fadeIn(settings.animation_speed); // Fade the new content
// Show the nav
if(isSet && _getFileType(pp_images[set_position])=="image") { $pp_pic_holder.find('.pp_hoverContainer').show(); }else{ $pp_pic_holder.find('.pp_hoverContainer').hide(); }
if(settings.allow_expand) {
if(pp_dimensions['resized']){ // Fade the resizing link if the image is resized
$('a.pp_expand,a.pp_contract').show();
}else{
$('a.pp_expand').hide();
}
}
if(settings.autoplay_slideshow && !pp_slideshow && !pp_open) $.prettyPhoto.startSlideshow();
settings.changepicturecallback(); // Callback!
pp_open = true;
});
_insert_gallery();
pp_settings.ajaxcallback();
};
/**
* Hide the content...DUH!
*/
function _hideContent(callback){
// Fade out the current picture
$pp_pic_holder.find('#pp_full_res object,#pp_full_res embed').css('visibility','hidden');
$pp_pic_holder.find('.pp_fade').fadeOut(settings.animation_speed,function(){
$('.pp_loaderIcon').show();
callback();
});
};
/**
* Check the item position in the gallery array, hide or show the navigation links
* @param setCount {integer} The total number of items in the set
*/
function _checkPosition(setCount){
(setCount > 1) ? $('.pp_nav').show() : $('.pp_nav').hide(); // Hide the bottom nav if it's not a set.
};
/**
* Resize the item dimensions if it's bigger than the viewport
* @param width {integer} Width of the item to be opened
* @param height {integer} Height of the item to be opened
* @return An array containin the "fitted" dimensions
*/
function _fitToViewport(width,height){
resized = false;
_getDimensions(width,height);
// Define them in case there's no resize needed
imageWidth = width, imageHeight = height;
if( ((pp_containerWidth > windowWidth) || (pp_containerHeight > windowHeight)) && doresize && settings.allow_resize && !percentBased) {
resized = true, fitting = false;
while (!fitting){
if((pp_containerWidth > windowWidth)){
imageWidth = (windowWidth - 200);
imageHeight = (height/width) * imageWidth;
}else if((pp_containerHeight > windowHeight)){
imageHeight = (windowHeight - 200);
imageWidth = (width/height) * imageHeight;
}else{
fitting = true;
};
pp_containerHeight = imageHeight, pp_containerWidth = imageWidth;
};
if((pp_containerWidth > windowWidth) || (pp_containerHeight > windowHeight)){
_fitToViewport(pp_containerWidth,pp_containerHeight)
};
_getDimensions(imageWidth,imageHeight);
};
return {
width:Math.floor(imageWidth),
height:Math.floor(imageHeight),
containerHeight:Math.floor(pp_containerHeight),
containerWidth:Math.floor(pp_containerWidth) + (settings.horizontal_padding * 2),
contentHeight:Math.floor(pp_contentHeight),
contentWidth:Math.floor(pp_contentWidth),
resized:resized
};
};
/**
* Get the containers dimensions according to the item size
* @param width {integer} Width of the item to be opened
* @param height {integer} Height of the item to be opened
*/
function _getDimensions(width,height){
width = parseFloat(width);
height = parseFloat(height);
// Get the details height, to do so, I need to clone it since it's invisible
$pp_details = $pp_pic_holder.find('.pp_details');
$pp_details.width(width);
detailsHeight = parseFloat($pp_details.css('marginTop')) + parseFloat($pp_details.css('marginBottom'));
$pp_details = $pp_details.clone().addClass(settings.theme).width(width).appendTo($('body')).css({
'position':'absolute',
'top':-10000
});
detailsHeight += $pp_details.height();
detailsHeight = (detailsHeight <= 34) ? 36 : detailsHeight; // Min-height for the details
$pp_details.remove();
// Get the titles height, to do so, I need to clone it since it's invisible
$pp_title = $pp_pic_holder.find('.ppt');
$pp_title.width(width);
titleHeight = parseFloat($pp_title.css('marginTop')) + parseFloat($pp_title.css('marginBottom'));
$pp_title = $pp_title.clone().appendTo($('body')).css({
'position':'absolute',
'top':-10000
});
titleHeight += $pp_title.height();
$pp_title.remove();
// Get the container size, to resize the holder to the right dimensions
pp_contentHeight = height + detailsHeight;
pp_contentWidth = width;
pp_containerHeight = pp_contentHeight + titleHeight + $pp_pic_holder.find('.pp_top').height() + $pp_pic_holder.find('.pp_bottom').height();
pp_containerWidth = width;
}
function _getFileType(itemSrc){
if (itemSrc.match(/youtube\.com\/watch/i) || itemSrc.match(/youtu\.be/i)) {
return 'youtube';
}else if (itemSrc.match(/vimeo\.com/i)) {
return 'vimeo';
}else if(itemSrc.match(/\b.mov\b/i)){
return 'quicktime';
}else if(itemSrc.match(/\b.swf\b/i)){
return 'flash';
}else if(itemSrc.match(/\biframe=true\b/i)){
return 'iframe';
}else if(itemSrc.match(/\bajax=true\b/i)){
return 'ajax';
}else if(itemSrc.match(/\bcustom=true\b/i)){
return 'custom';
}else if(itemSrc.substr(0,1) == '#'){
return 'inline';
}else{
return 'image';
};
};
function _center_overlay(){
if(doresize && typeof $pp_pic_holder != 'undefined') {
scroll_pos = _get_scroll();
contentHeight = $pp_pic_holder.height(), contentwidth = $pp_pic_holder.width();
projectedTop = (windowHeight/2) + scroll_pos['scrollTop'] - (contentHeight/2);
if(projectedTop < 0) projectedTop = 0;
if(contentHeight > windowHeight)
return;
$pp_pic_holder.css({
'top': projectedTop,
'left': (windowWidth/2) + scroll_pos['scrollLeft'] - (contentwidth/2)
});
};
};
function _get_scroll(){
if (self.pageYOffset) {
return {scrollTop:self.pageYOffset,scrollLeft:self.pageXOffset};
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
return {scrollTop:document.documentElement.scrollTop,scrollLeft:document.documentElement.scrollLeft};
} else if (document.body) {// all other Explorers
return {scrollTop:document.body.scrollTop,scrollLeft:document.body.scrollLeft};
};
};
function _resize_overlay() {
windowHeight = $(window).height(), windowWidth = $(window).width();
if(typeof $pp_overlay != "undefined") $pp_overlay.height($(document).height()).width(windowWidth);
};
function _insert_gallery(){
if(isSet && settings.overlay_gallery && _getFileType(pp_images[set_position])=="image") {
itemWidth = 52+5; // 52 beign the thumb width, 5 being the right margin.
navWidth = (settings.theme == "facebook" || settings.theme == "pp_default") ? 50 : 30; // Define the arrow width depending on the theme
itemsPerPage = Math.floor((pp_dimensions['containerWidth'] - 100 - navWidth) / itemWidth);
itemsPerPage = (itemsPerPage < pp_images.length) ? itemsPerPage : pp_images.length;
totalPage = Math.ceil(pp_images.length / itemsPerPage) - 1;
// Hide the nav in the case there's no need for links
if(totalPage == 0){
navWidth = 0; // No nav means no width!
$pp_gallery.find('.pp_arrow_next,.pp_arrow_previous').hide();
}else{
$pp_gallery.find('.pp_arrow_next,.pp_arrow_previous').show();
};
galleryWidth = itemsPerPage * itemWidth;
fullGalleryWidth = pp_images.length * itemWidth;
// Set the proper width to the gallery items
$pp_gallery
.css('margin-left',-((galleryWidth/2) + (navWidth/2)))
.find('div:first').width(galleryWidth+5)
.find('ul').width(fullGalleryWidth)
.find('li.selected').removeClass('selected');
goToPage = (Math.floor(set_position/itemsPerPage) < totalPage) ? Math.floor(set_position/itemsPerPage) : totalPage;
$.prettyPhoto.changeGalleryPage(goToPage);
$pp_gallery_li.filter(':eq('+set_position+')').addClass('selected');
}else{
$pp_pic_holder.find('.pp_content').unbind('mouseenter mouseleave');
// $pp_gallery.hide();
}
}
function _build_overlay(caller){
// Inject Social Tool markup into General markup
if(settings.social_tools)
facebook_like_link = settings.social_tools.replace('{location_href}', encodeURIComponent(location.href));
settings.markup = settings.markup.replace('{pp_social}','');
$('body').append(settings.markup); // Inject the markup
$pp_pic_holder = $('.pp_pic_holder') , $ppt = $('.ppt'), $pp_overlay = $('div.pp_overlay'); // Set my global selectors
// Inject the inline gallery!
if(isSet && settings.overlay_gallery) {
currentGalleryPage = 0;
toInject = "";
for (var i=0; i < pp_images.length; i++) {
if(!pp_images[i].match(/\b(jpg|jpeg|png|gif)\b/gi)){
classname = 'default';
img_src = '';
}else{
classname = '';
img_src = pp_images[i];
}
toInject += "<li class='"+classname+"'><a href='#'><img src='" + img_src + "' width='50' alt='' /></a></li>";
};
toInject = settings.gallery_markup.replace(/{gallery}/g,toInject);
$pp_pic_holder.find('#pp_full_res').after(toInject);
$pp_gallery = $('.pp_pic_holder .pp_gallery'), $pp_gallery_li = $pp_gallery.find('li'); // Set the gallery selectors
$pp_gallery.find('.pp_arrow_next').click(function(){
$.prettyPhoto.changeGalleryPage('next');
$.prettyPhoto.stopSlideshow();
return false;
});
$pp_gallery.find('.pp_arrow_previous').click(function(){
$.prettyPhoto.changeGalleryPage('previous');
$.prettyPhoto.stopSlideshow();
return false;
});
$pp_pic_holder.find('.pp_content').hover(
function(){
$pp_pic_holder.find('.pp_gallery:not(.disabled)').fadeIn();
},
function(){
$pp_pic_holder.find('.pp_gallery:not(.disabled)').fadeOut();
});
itemWidth = 52+5; // 52 beign the thumb width, 5 being the right margin.
$pp_gallery_li.each(function(i){
$(this)
.find('a')
.click(function(){
$.prettyPhoto.changePage(i);
$.prettyPhoto.stopSlideshow();
return false;
});
});
};
// Inject the play/pause if it's a slideshow
if(settings.slideshow){
$pp_pic_holder.find('.pp_nav').prepend('<a href="#" class="pp_play">Play</a>')
$pp_pic_holder.find('.pp_nav .pp_play').click(function(){
$.prettyPhoto.startSlideshow();
return false;
});
}
$pp_pic_holder.attr('class','pp_pic_holder ' + settings.theme); // Set the proper theme
$pp_overlay
.css({
'opacity':0,
'height':$(document).height(),
'width':$(window).width()
})
.bind('click',function(){
if(!settings.modal) $.prettyPhoto.close();
});
$('a.pp_close').bind('click',function(){ $.prettyPhoto.close(); return false; });
if(settings.allow_expand) {
$('a.pp_expand').bind('click',function(e){
// Expand the image
if($(this).hasClass('pp_expand')){
$(this).removeClass('pp_expand').addClass('pp_contract');
doresize = false;
}else{
$(this).removeClass('pp_contract').addClass('pp_expand');
doresize = true;
};
_hideContent(function(){ $.prettyPhoto.open(); });
return false;
});
}
$pp_pic_holder.find('.pp_previous, .pp_nav .pp_arrow_previous').bind('click',function(){
$.prettyPhoto.changePage('previous');
$.prettyPhoto.stopSlideshow();
return false;
});
$pp_pic_holder.find('.pp_next, .pp_nav .pp_arrow_next').bind('click',function(){
$.prettyPhoto.changePage('next');
$.prettyPhoto.stopSlideshow();
return false;
});
_center_overlay(); // Center it
};
if(!pp_alreadyInitialized && getHashtag()){
pp_alreadyInitialized = true;
// Grab the rel index to trigger the click on the correct element
hashIndex = getHashtag();
hashRel = hashIndex;
hashIndex = hashIndex.substring(hashIndex.indexOf('/')+1,hashIndex.length-1);
hashRel = hashRel.substring(0,hashRel.indexOf('/'));
// Little timeout to make sure all the prettyPhoto initialize scripts has been run.
// Useful in the event the page contain several init scripts.
setTimeout(function(){ $("a["+pp_settings.hook+"^='"+hashRel+"']:eq("+hashIndex+")").trigger('click'); },50);
}
return this.unbind('click.prettyphoto').bind('click.prettyphoto',$.prettyPhoto.initialize); // Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
};
function getHashtag(){
var url = location.href;
hashtag = (url.indexOf('#prettyPhoto') !== -1) ? decodeURI(url.substring(url.indexOf('#prettyPhoto')+1,url.length)) : false;
if(hashtag){ hashtag = hashtag.replace(/<|>/g,''); }
return hashtag;
};
function setHashtag(){
if(typeof theRel == 'undefined') return; // theRel is set on normal calls, it's impossible to deeplink using the API
location.hash = theRel + '/'+rel_index+'/';
};
function clearHashtag(){
if ( location.href.indexOf('#prettyPhoto') !== -1 ) location.hash = "prettyPhoto";
}
function getParam(name,url){
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( url );
return ( results == null ) ? "" : results[1];
}
})(jQuery);
var pp_alreadyInitialized = false; // Used for the deep linking to make sure not to call the same function several times.

File diff suppressed because one or more lines are too long

View File

@@ -1,33 +0,0 @@
// Dialog zur Anzeige der Datenschutz-Erklärung
$(document).ready(function() {
// Klick auf den Anleitungs-Button
$('#dschu').click(function() {
$("#datenschutz").dialog('open');
return false;
});
$("#datenschutz").dialog({
autoOpen: false,
width: 800,
modal: true,
position: 'center',
title: 'Datenschutz',
open:
function() {
$(this).load('datenschutztext.php');
},
buttons: [
{
text: "Schließen",
click : function() {
$(this).dialog("close");
},
width: 150,
}
],
});
});

View File

@@ -1,100 +0,0 @@
// sternwarte.js rxf 2020-09-25
// Diverse Hilfsroutinen für die einzelnen Seiten
//
$(document).ready(() => {
let maintenance = false;
// Optionen für die Anzeige der Fotos
// $("a[rel^='prettyPhoto']").prettyPhoto({
// animationSpeed: 'normal', /* fast/slow/normal */
// padding: 20, /* padding for each side of the picture */
// opacity: 1, /* Value betwee 0 and 1 */
// showTitle: false, /* true/false */
// allowresize: true, /* true/false */
// counter_separator_label: '/', /* The separator for the gallery counter 1 "of" 2 */
// theme: 'dark_rounded', /* light_rounded / dark_rounded / light_square / dark_square */
// callback: function () {
// }
// });
// Klick auf den Datenschutz-Button
$('#dschu').click(function() {
$("#datenschutz").dialog('open');
return false;
});
// Anzeige der Datenschutzerklärung
$("#datenschutz").dialog({
autoOpen: false,
width: 800,
modal: true,
position: 'center',
title: 'Datenschutz',
open:
function() {
$(this).load('datenschutztext.html');
},
buttons: [
{
text: "Schließen",
click : function() {
$(this).dialog("close");
},
width: 150,
}
],
});
// Click auf 'Zeiten' auf der HOME-Page: Dialogbox öffnen
$('a[href="#zeiten"]').click(() => {
$("#fzeiten").dialog('open');
return false;
});
// Anzeige der Führungszeiten
$("#fzeiten").dialog({
autoOpen: false,
width: 800,
modal: true,
position: 'center',
title: 'Führungszeiten',
open:
function() {
$(this).load('fuehrungen_txt.html');
},
buttons: [
{
text: "Schließen",
click : function() {
$(this).dialog("close");
},
width: 150,
}
],
});
// ggf. den Maintenance-Dialog einblenden
var dialogMaint = $('#maintdialog').dialog({
autoOpen:false,
closeOnEscape: false,
open: function(event, ui) {
$(".ui-dialog-titlebar-close").hide();
$(this).load('maint.html', function() {
});
},
width:800,
modal: true,
resizable:false,
position: {my:'center top+30%', at: 'center top+30%'},
});
if (maintenance == true) {
dialogMaint.dialog('open');
}
});

View File

@@ -47,3 +47,7 @@ body {
#beonamen { #beonamen {
font-size: 80%; font-size: 80%;
} }
.fc {
--fc-today-bg-color: rgba(129, 222, 201, 0.75);
}

View File

@@ -11,6 +11,8 @@ function sendmail($subject, $from, $body, $cc=[], $bcc=[], $to=[]) {
$ret = []; $ret = [];
$ret['error'] = false; $ret['error'] = false;
$develop = 'true';
$mail = new PHPMailer(true); $mail = new PHPMailer(true);
try { try {
@@ -65,7 +67,11 @@ function sendmail($subject, $from, $body, $cc=[], $bcc=[], $to=[]) {
// BCC // BCC
if (count($bcc) != 0) { if (count($bcc) != 0) {
foreach ($bcc as $bc) { foreach ($bcc as $bc) {
$mail->addBCC($bc); if ($develop == 'true') {
$mail->addCC($bc);
} else {
$mail->addBCC($bc);
}
} }
} }

View File

@@ -252,8 +252,6 @@
"$stern_vorname $stern_name um $stern_zeit Uhr " . "$stern_vorname $stern_name um $stern_zeit Uhr " .
"für $stern_teil $person." . "für $stern_teil $person." .
"\r\n\r\n" . "\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei klarem Himmel statt. Falls der Himmel bedeckt ist " . "Die Führung findet NUR bei klarem Himmel statt. Falls der Himmel bedeckt ist " .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " . "und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " .
"eine Email.\r\n\r\n" . "eine Email.\r\n\r\n" .
@@ -269,8 +267,6 @@
"auf der Sternwarte Welzheim für</p>" . "auf der Sternwarte Welzheim für</p>" .
"<p style='text-align: center;'>$stern_vorname $stern_name um $stern_zeit Uhr " . "<p style='text-align: center;'>$stern_vorname $stern_name um $stern_zeit Uhr " .
"für $stern_teil $person.</p>" . "für $stern_teil $person.</p>" .
"<p>Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit.<br />".
"Ohne diese Bestätigung erfolgt ausnahmslos <strong>kein</strong> Einlass.</p>" .
"<p>Die Führung findet <strong>nur</strong> bei klarem Himmel statt. Falls der Himmel bedeckt ist " . "<p>Die Führung findet <strong>nur</strong> bei klarem Himmel statt. Falls der Himmel bedeckt ist " .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " . "und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " .
"eine Email.<p>" . "eine Email.<p>" .
@@ -357,8 +353,7 @@
</p> </p>
<p> <p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf), Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung erhalten Sie eine Anmeldebestätigung per e-mail.
ausgedruckt oder in digitaler Form mitbringen!
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos
k e i n Einlass.</strong> k e i n Einlass.</strong>
@@ -472,7 +467,7 @@
<p> <p>
Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a> Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a>
</p> </p>
<p class="lastchange">Letzte Änderungen: 2022-08-17 rxf</p> <p class="lastchange">Letzte Änderungen: 2025-12-01 rxf</p>
</form> </form>
</div> </div>

View File

@@ -2,7 +2,7 @@
.storno { .storno {
font-size: 14px; font-size: 14px;
min-height: 500px; min-height: 550px;
position: relative; position: relative;
} }
@@ -131,3 +131,8 @@ h5 {
margin-left: 10px; margin-left: 10px;
font-size: 80%; font-size: 80%;
} }
#abbrechen {
background-color: gray;
margin-top: 40px;
}

View File

@@ -51,7 +51,7 @@
Für diese E-Mail Adresse ist keine Führung angemeldet !<br /> Für diese E-Mail Adresse ist keine Führung angemeldet !<br />
</div> </div>
<div id="anmeldid" class="col-12 col-xm-8 text-center"> <div id="anmeldid" class="col-12 col-xm-8 text-center">
Sie sind angemeldet für: <br /><br /> Sie sind angemeldet für eine <span id="fart">Sternführung</span> am: <br /><br />
<div id="anmeldung"> <div id="anmeldung">
2022-07-12 22:00 Uhr 4 Personen 2022-07-12 22:00 Uhr 4 Personen
</div> </div>

View File

@@ -5,8 +5,8 @@
$(document).ready(() => { $(document).ready(() => {
// Globale Konstanten und Variable // Globale Konstanten und Variable
// const ajaxURL="php/anmeldDB.php"; // const ajaxURL="php/anmeldDB.php";
const ajaxURL="../../DB4js.php"; const ajaxURL = "../../DB4js_all.php";
const maxVisitors = 25 const maxVisitors = 25
const months2add = 3 const months2add = 3
@@ -21,9 +21,9 @@ $(document).ready(() => {
// Return: // Return:
// angeforderte Daten als JSON // angeforderte Daten als JSON
const fetchFromDbase = async (body) => { const fetchFromDbase = async (body) => {
const response = await fetch(ajaxURL,{ const response = await fetch(ajaxURL, {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/js'}, headers: { 'Content-Type': 'application/js' },
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
let rt = await response.json(); let rt = await response.json();
@@ -37,22 +37,41 @@ $(document).ready(() => {
// Return: // Return:
// angeforderte Daten als JSON // angeforderte Daten als JSON
const putToDbase = async (body) => { const putToDbase = async (body) => {
const response = await fetch(ajaxURL,{ const response = await fetch(ajaxURL, {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/js'}, headers: { 'Content-Type': 'application/js' },
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
return await response.json(); return await response.json();
} }
// Wochentag aus integer Datum extrahieren
function getWochentag(datumInt) {
const d = new Date(
Math.floor(datumInt / 10000), // Jahr
Math.floor((datumInt % 10000) / 100) - 1, // Monat (0-basiert)
datumInt % 100 // Tag
);
return ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"][d.getDay()];
}
// Aus dem integer Führungsdatum (aus anmeldungen) den Wochentag und
// die Uhrzeit aus der Tabelle fdatum 1 holen
const getFuhrzeit = async (dt, typ) => {
return await fetchFromDbase({ cmd: 'GET_TIME_BY_DATE', dt: dt, typ: typ })
}
// Das Führungsdatum extrahieren // Das Führungsdatum extrahieren
const buildDatum = async (tn, short) => { const buildDatum = async (tn, short) => {
const person = tn.anzahl === '1' ? 'Person' : 'Personen' const person = tn.anzahl === '1' ? 'Person' : 'Personen'
const datum = await fetchFromDbase({cmd: 'GET_ONE_DATE', fid: tn.fid}) const uhrzeit = await getFuhrzeit(tn.fdatum, tn.typ)
if(short) { if (short) {
return `${moment(datum.datum).format('DD.MM.YYYY')}` return `${moment(tn.fdatum).format('DD.MM.YYYY')}`
} }
return `${datum.wtag}, den ${moment(datum.datum).format('DD.MM.YYYY')} um ${datum.uhrzeit} mit ${tn.anzahl} ${person}` if(tn.typ === 'sonnen') {
document.getElementById('fart').innerHTML = "Sonnenführung"
}
return `${getWochentag(tn.fdatum)}, den ${moment(tn.fdatum).format('DD.MM.YYYY')} um ${uhrzeit} mit ${tn.anzahl} ${person}`
} }
// Das Führungsdatum anzeigen // Das Führungsdatum anzeigen
@@ -67,13 +86,13 @@ $(document).ready(() => {
} }
const austragen = async (teilnehmer) => { const austragen = async (teilnehmer) => {
// console.log("Austragen von ", teilnehmer) // console.log("Austragen von ", teilnehmer)
// return // return
oldtndata = {...teilnehmer} oldtndata = { ...teilnehmer }
let delstr = {cmd: 'DELETE_ENTRY', id: parseInt(teilnehmer.id)} let delstr = { cmd: 'DELETE_ENTRY', id: parseInt(teilnehmer.id) }
const erg1 = await putToDbase(delstr) const erg1 = await putToDbase(delstr)
console.log("Storno Ergebnis: ",erg1) console.log("Storno Ergebnis: ", erg1)
if(erg1) { if (erg1) {
$('#ausgetragen').show() $('#ausgetragen').show()
$('#lower_text').show() $('#lower_text').show()
sendEmail(teilnehmer, true) sendEmail(teilnehmer, true)
@@ -84,44 +103,48 @@ $(document).ready(() => {
} }
const umbuchen = async (teilnehmer) => { const umbuchen = async (teilnehmer) => {
// console.log('Umbuchen von ',teilnehmer) // console.log('Umbuchen von ',teilnehmer)
// return // return
oldtndata = {...teilnehmer} oldtndata = { ...teilnehmer }
let start = moment() let start = moment()
start = start.add(vorlauf, 'h') start = start.add(vorlauf, 'h')
let end = moment() let end = moment()
end = end.add(months2add, 'M') let add = months2add
if (teilnehmer.typ === 'sonnen') {
add = 12
}
end = end.add(add, 'M')
const anzahl = parseInt(teilnehmer.anzahl) const anzahl = parseInt(teilnehmer.anzahl)
let fuehrungen = await fetchFromDbase({cmd: 'GET_FUEHRUNGEN', start: start.format('YYYYMMDD'), end: end.format('YYYYMMDD')}) let fuehrungen = await fetchFromDbase({ cmd: 'GET_FUEHRUNGEN', start: start.format('YYYYMMDD'), end: end.format('YYYYMMDD'), typ: teilnehmer.typ})
let r = `<label for "ftermin" class="labeltext"><strong>Umbuchung auf</strong></label><br /><select name="ftermin" id="ftermin"><option>-- Bitte wählen Sie ein Datum aus--</option>` let r = `<label for "ftermin" class="labeltext"><strong>Umbuchung auf</strong></label><br /><select name="ftermin" id="ftermin"><option>-- Bitte wählen Sie ein Datum aus--</option>`
for (let f of fuehrungen) { for (let f of fuehrungen) {
if(f.datum == teilnehmer.fdatum) { if (f.datum == teilnehmer.fdatum) {
continue continue
} }
let count = await fetchFromDbase({cmd: 'GET_COUNTS_DATE', date: f.datum}) let count = await fetchFromDbase({ cmd: 'GET_COUNTS_DATE', date: f.datum })
count = count ? parseInt(count) : 0 count = count ? parseInt(count) : 0
if(count + anzahl >= maxVisitors) { if (count + anzahl >= maxVisitors) {
continue // wenn der Platz nicht reicht, nicht anzeigen continue // wenn der Platz nicht reicht, nicht anzeigen
} }
r += `<option id=${f.id} value=${f.datum}>` r += `<option id=${f.id} value=${f.datum}>`
r += `${f.wtag.substring(0,2)} , ${moment(f.datum).format('DD.MM.YYYY')} ${f.uhrzeit} ` r += `${getWochentag(f.datum).substring(0, 2)} , ${moment(f.datum).format('DD.MM.YYYY')} ${await getFuhrzeit(f.datum, teilnehmer.typ)} `
r += `Frei ${(maxVisitors-count) > 0 ? maxVisitors-count : 0}</option>` r += `Frei ${(maxVisitors - count) > 0 ? maxVisitors - count : 0}</option>`
} }
r += '</select>' r += '</select>'
$('#umbuchung').html(r) $('#umbuchung').html(r)
// $('#umgebucht').html('Bitte wählen Sie ein freies Datum über den kleine Pfeil rechts.') // $('#umgebucht').html('Bitte wählen Sie ein freies Datum über den kleine Pfeil rechts.')
$('#umgebucht').show() $('#umgebucht').show()
// Eventhandler für Auswahl eines Datums über die SelectBox: // Eventhandler für Auswahl eines Datums über die SelectBox:
// Anzeigen der Anmeldungen dazu // Anzeigen der Anmeldungen dazu
$('#ftermin').change( async () => { $('#ftermin').change(async () => {
const x = $('#ftermin').find(':selected'); const x = $('#ftermin').find(':selected');
const date = x[0].value const date = x[0].value
teilnehmer.fdatum = date teilnehmer.fdatum = date
teilnehmer.fid = x[0].id teilnehmer.fid = x[0].id
const update = {cmd: 'UPDATETLNFD', fdatum: date, fid: x[0].id, id: teilnehmer.id} const update = { cmd: 'UPDATETLNFD', fdatum: date, fid: x[0].id, id: teilnehmer.id }
const erg = await putToDbase(update) const erg = await putToDbase(update)
if(erg) { if (erg) {
$('#umbuchung').hide() $('#umbuchung').hide()
$('#umgebucht').html(`Sie wurden erfolgreich umgebucht auf <br /><br />${await buildDatum(teilnehmer, false)}`) $('#umgebucht').html(`Sie wurden erfolgreich umgebucht auf <br /><br />${await buildDatum(teilnehmer, false)}`)
$('#umgebucht').show() $('#umgebucht').show()
@@ -137,11 +160,11 @@ $(document).ready(() => {
} }
const aendern = async (teilnehmer) => { const aendern = async (teilnehmer) => {
console.log('Ändern von ',teilnehmer) console.log('Ändern von ', teilnehmer)
let count = await fetchFromDbase({cmd: 'GET_COUNTS_DATE', date: teilnehmer.fdatum}) let count = await fetchFromDbase({ cmd: 'GET_COUNTS_DATE', date: teilnehmer.fdatum })
let anzahl = parseInt(teilnehmer.anzahl) let anzahl = parseInt(teilnehmer.anzahl)
let max = anzahl let max = anzahl
if(count < maxVisitors) { if (count < maxVisitors) {
max = maxVisitors - (count - anzahl) max = maxVisitors - (count - anzahl)
} }
if (max > 10) { if (max > 10) {
@@ -149,24 +172,24 @@ $(document).ready(() => {
} }
console.log("Max: ", max) console.log("Max: ", max)
$('#maxanzahl').text(`max. ${max}`) $('#maxanzahl').text(`max. ${max}`)
$('#personen').attr({max: max, min: 1, step: 1, value: anzahl}) $('#personen').attr({ max: max, min: 1, step: 1, value: anzahl })
// Evelthandler Übernehmen geklicked // Evelthandler Übernehmen geklicked
$('#bsave').click(async () => { $('#bsave').click(async () => {
$('#save').hide() $('#save').hide()
teilnehmer.anzahl = $('#personen').val() teilnehmer.anzahl = $('#personen').val()
let e = await putToDbase({cmd: 'UPDATE_TLN', data: teilnehmer, id: teilnehmer.id}) let e = await putToDbase({ cmd: 'UPDATE_TLN', data: teilnehmer, id: teilnehmer.id })
$('#newperson').html(`Sie sind nun mit <strong>${teilnehmer.anzahl} </strong> Personen angemeldet.`) $('#newperson').html(`Sie sind nun mit <strong>${teilnehmer.anzahl} </strong> Personen angemeldet.`)
$('#bsave').hide() $('#bsave').hide()
$('#newperson').show() $('#newperson').show()
$('#homebutton').show() $('#homebutton').show()
}) })
} }
// Emailadresse eingegeben // Emailadresse eingegeben
const setEvent = (f) => { const setEvent = (f) => {
$(document).on("keypress", "input", async function (e) { $(document).on("keypress", "input", async function (e) {
if (e.which === 13) { if (e.which === 13) {
@@ -196,15 +219,15 @@ $(document).ready(() => {
$('#umbuchen').click(() => { $('#umbuchen').click(() => {
umbuchen(f[idx]) umbuchen(f[idx])
$('#anmeldid').hide() $('#anmeldid').hide()
// $('#email').val("") // $('#email').val("")
}) })
// Ändern geklicked // Ändern geklicked
$('#aendern').click(() => { $('#aendern').click(() => {
aendern(f[idx]) aendern(f[idx])
$('#butgroup').hide() $('#butgroup').hide()
$('#aenderung').show() $('#aenderung').show()
// $('#anmeldid').hide() // $('#anmeldid').hide()
// $('#email').val("") // $('#email').val("")
}) })
} }
}) })
@@ -214,20 +237,18 @@ $(document).ready(() => {
sendEmail = async (tln, storno) => { sendEmail = async (tln, storno) => {
let fdatum = await buildDatum(tln, false) let fdatum = await buildDatum(tln, false)
let oldfdatum = await buildDatum(oldtndata, true) let oldfdatum = await buildDatum(oldtndata, true)
let subject = `${storno ? "Stornierung":"Umbuchung"} der Führung vom ${oldfdatum} auf der Sternwarte Welzheim` let subject = `${storno ? "Stornierung" : "Umbuchung"} der Führung vom ${oldfdatum} auf der Sternwarte Welzheim`
let body_txt = ` let body_txt = `
Sehr geehrte Dame, sehr geehrter Herr, Sehr geehrte Dame, sehr geehrter Herr,
hiermit bestätigen wir die ${storno ? 'Stornierung' : 'Umbuchung'} Ihrer Führung auf der Sternwarte Welzheim vom` hiermit bestätigen wir die ${storno ? 'Stornierung' : 'Umbuchung'} Ihrer Führung auf der Sternwarte Welzheim vom`
if(!storno) { if (!storno) {
body_txt += ` ${oldfdatum}. body_txt += ` ${oldfdatum}.
Sie wurden umgebucht auf: Sie wurden umgebucht auf:
${fdatum} ${fdatum}
Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit.
Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist
und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Führungsbeginn und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Führungsbeginn
eine Email. Sie können sich dann gerne zu einer anderen Führung neu anmelden. eine Email. Sie können sich dann gerne zu einer anderen Führung neu anmelden.
@@ -244,12 +265,12 @@ Mit freundlichen Grüßen
Beobachterteam der Sternwarte Welzheim Beobachterteam der Sternwarte Welzheim
www.sternwarte-welzheim.de www.sternwarte-welzheim.de
` `
let erg = await putToDbase({cmd: 'SEND_MAIL_HTML', subject: subject, to: [tln.email], body_txt: body_txt, body_html: ""}) let erg = await putToDbase({ cmd: 'SEND_MAIL_HTML', subject: subject, to: [tln.email], body_txt: body_txt, body_html: "" })
console.log("Antwort von sendmail_1: ", erg) console.log("Antwort von sendmail_1: ", erg)
body_txt = `Die Führung vom ${oldfdatum} wurde ${storno ? 'storniert' : 'umgebucht'} body_txt = `Die Führung vom ${oldfdatum} wurde ${storno ? 'storniert' : 'umgebucht'}
` `
if(!storno) { if (!storno) {
body_txt += ` body_txt += `
auf ${fdatum}` auf ${fdatum}`
} }
@@ -258,12 +279,12 @@ auf ${fdatum}`
Besucher: ${tln.name} ${tln.vorname}` Besucher: ${tln.name} ${tln.vorname}`
body_html = `Die Führung vom ${oldfdatum} wurde ${storno ? 'storniert' : 'umgebucht'}` body_html = `Die Führung vom ${oldfdatum} wurde ${storno ? 'storniert' : 'umgebucht'}`
if(!storno) { if (!storno) {
body_html += ` auf ${fdatum}<br />` body_html += ` auf ${fdatum}<br />`
} }
body_html += `<br />Besucher: ${tln.name} ${tln.vorname}` body_html += `<br />Besucher: ${tln.name} ${tln.vorname}`
erg = await putToDbase({cmd: 'SEND_MAIL_HTML', subject: subject, to: ['rexfue@gmail.com'], body_txt: body_txt, body_html: body_html}) erg = await putToDbase({ cmd: 'SEND_MAIL_HTML', subject: subject, to: ['rexfue@gmail.com'], body_txt: body_txt, body_html: body_html })
console.log("Antwort von sendmail_2: ", erg) console.log("Antwort von sendmail_2: ", erg)
} }
@@ -277,7 +298,7 @@ Besucher: ${tln.name} ${tln.vorname}`
// und anzeigen // und anzeigen
// Params: // Params:
// n -> Anzahl der zu holnden Daten // n -> Anzahl der zu holnden Daten
async function main(){ async function main() {
console.log("Running...") console.log("Running...")
$('.lastchange').text(`Letzte Änderungen: ${VDATE} rxf`) $('.lastchange').text(`Letzte Änderungen: ${VDATE} rxf`)
$('#anmeldid').hide() $('#anmeldid').hide()
@@ -290,7 +311,7 @@ Besucher: ${tln.name} ${tln.vorname}`
$('#versn').html("Version: " + VERSION + ' vom ' + VDATE); $('#versn').html("Version: " + VERSION + ' vom ' + VDATE);
let fdatum = moment().format('YYYYMMDD') let fdatum = moment().format('YYYYMMDD')
// alle Anmeldungen ab fdatum in ein Array holen // alle Anmeldungen ab fdatum in ein Array holen
let fuehrungen = await fetchFromDbase({cmd: 'GET_ALLTEILN', fdatum: fdatum}) let fuehrungen = await fetchFromDbase({ cmd: 'GET_ALLTEILN', fdatum: fdatum })
setEvent(fuehrungen) setEvent(fuehrungen)
} }

View File

@@ -0,0 +1,147 @@
#!/usr/bin/env node
/*
Integration smoke tests for DB4js_all.php (CommonJS)
Env:
- API_URL (default: https://sternwarte-welzheim.de/DB4js_all.php)
- API_USER / API_PASS (optional Basic Auth)
*/
const https = require('https');
const http = require('http');
const { URL } = require('url');
const API_URL = process.env.API_URL || 'https://sternwarte-welzheim.de/DB4js_all.php';
const API_USER = process.env.API_USER || '';
const API_PASS = process.env.API_PASS || '';
function postJSON(urlStr, bodyObj) {
return new Promise((resolve, reject) => {
const url = new URL(urlStr);
const body = JSON.stringify(bodyObj || {});
const isHttps = url.protocol === 'https:';
const lib = isHttps ? https : http;
const headers = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body)
};
if (API_USER && API_PASS) {
const token = Buffer.from(`${API_USER}:${API_PASS}`).toString('base64');
headers['Authorization'] = `Basic ${token}`;
}
const req = lib.request({
method: 'POST',
hostname: url.hostname,
port: url.port || (isHttps ? 443 : 80),
path: url.pathname + url.search,
headers,
rejectUnauthorized: false,
timeout: 15000,
}, (res) => {
let data = '';
res.setEncoding('utf8');
res.on('data', c => data += c);
res.on('end', () => {
let json = null;
try { json = JSON.parse(data); } catch (e) {}
if (res.statusCode >= 400) {
return reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
if (!json) return reject(new Error(`Non-JSON response: ${data}`));
resolve(json);
});
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(new Error('Request timeout')); });
req.write(body);
req.end();
});
}
function assert(cond, msg) {
if (!cond) throw new Error(msg);
}
(async () => {
const failures = [];
let step = 0;
function record(name, fn) {
return Promise.resolve()
.then(fn)
.then(() => console.log(`${++step}. ${name}`))
.catch((err) => { failures.push({ name, err }); console.error(`${++step}. ${name} ->`, err.message); });
}
console.log(`API_URL=${API_URL}`);
await record('PING returns pong + timestamp', async () => {
const r = await postJSON(API_URL, { cmd: 'PING' });
assert(r && r.pong === true, 'pong !== true');
assert(typeof r.timestamp === 'string' && r.timestamp.length > 0, 'timestamp missing');
});
await record('LIST_COMMANDS returns command map', async () => {
const r = await postJSON(API_URL, { cmd: 'LIST_COMMANDS' });
assert(r && r.commands && typeof r.count === 'number', 'invalid LIST_COMMANDS payload');
});
await record('GET_BEOS with alias email works', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_BEOS', onlyguides: true, what: 'id,name,email' });
assert(Array.isArray(r), 'GET_BEOS did not return array');
if (r.length > 0) {
const it = r[0];
assert('id' in it, 'beo item missing id');
assert('name' in it, 'beo item missing name');
assert(('email' in it) || ('email_1' in it), 'beo item missing email/email_1');
}
});
await record('GET_BEOS with non-existing field falls back', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_BEOS', onlyguides: 'true', what: 'id,name,doesnotexist' });
assert(Array.isArray(r), 'GET_BEOS did not return array on fallback');
});
let sampleDate = null;
await record('GET_TERMINE returns array', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_TERMINE' });
assert(Array.isArray(r), 'GET_TERMINE did not return array');
if (r.length > 0) {
const d = r.find(x => typeof x.datum === 'string');
if (d) sampleDate = d.datum;
}
});
if (sampleDate) {
await record('GET_TIME for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_TIME', date: sampleDate });
assert(r && typeof r.time === 'string', 'GET_TIME missing time');
});
let sampleFid = null;
await record('GET_FID for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_FID', datum: sampleDate });
assert(r && ('fid' in r), 'GET_FID missing fid');
sampleFid = r.fid;
});
await record('GET_COUNTS_DATE for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_COUNTS_DATE', date: sampleDate });
assert(r && typeof r.count === 'number', 'GET_COUNTS_DATE invalid');
});
if (sampleFid) {
await record('GET_COUNTS for sample fid', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_COUNTS', fid: sampleFid });
assert(r && typeof r.count === 'number', 'GET_COUNTS invalid');
});
}
}
if (failures.length) {
console.error(`\n${failures.length} test(s) failed:`);
failures.forEach((f, i) => console.error(` ${i + 1}) ${f.name}: ${f.err && f.err.stack || f.err}`));
process.exit(1);
} else {
console.log('\nAll integration tests passed.');
}
})().catch((e) => { console.error('Fatal error:', e); process.exit(1); });

View File

@@ -0,0 +1,151 @@
#!/usr/bin/env node
/*
Integration smoke tests for DB4js_all.php
- Uses HTTPS POST JSON to remote API (or custom API_URL)
- No external deps (Node core only)
Env:
- API_URL (default: https://sternwarte-welzheim.de/DB4js_all.php)
- API_USER / API_PASS (optional Basic Auth)
*/
const https = require('https');
const http = require('http');
const { URL } = require('url');
const API_URL = process.env.API_URL || 'https://sternwarte-welzheim.de/DB4js_all.php';
const API_USER = process.env.API_USER || '';
const API_PASS = process.env.API_PASS || '';
function postJSON(urlStr, bodyObj) {
return new Promise((resolve, reject) => {
const url = new URL(urlStr);
const body = JSON.stringify(bodyObj || {});
const isHttps = url.protocol === 'https:';
const lib = isHttps ? https : http;
const headers = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body)
};
if (API_USER && API_PASS) {
const token = Buffer.from(`${API_USER}:${API_PASS}`).toString('base64');
headers['Authorization'] = `Basic ${token}`;
}
const req = lib.request({
method: 'POST',
hostname: url.hostname,
port: url.port || (isHttps ? 443 : 80),
path: url.pathname + url.search,
headers,
rejectUnauthorized: false, // allow self-signed (if any)
timeout: 15000,
}, (res) => {
let data = '';
res.setEncoding('utf8');
res.on('data', c => data += c);
res.on('end', () => {
let json = null;
try { json = JSON.parse(data); } catch (e) {}
if (res.statusCode >= 400) {
return reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
if (!json) return reject(new Error(`Non-JSON response: ${data}`));
resolve(json);
});
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(new Error('Request timeout')); });
req.write(body);
req.end();
});
}
function assert(cond, msg) {
if (!cond) throw new Error(msg);
}
(async () => {
const failures = [];
let step = 0;
function record(name, fn) {
return Promise.resolve()
.then(fn)
.then(() => console.log(`${++step}. ${name}`))
.catch((err) => { failures.push({ name, err }); console.error(`${++step}. ${name} ->`, err.message); });
}
console.log(`API_URL=${API_URL}`);
await record('PING returns pong + timestamp', async () => {
const r = await postJSON(API_URL, { cmd: 'PING' });
assert(r && r.pong === true, 'pong !== true');
assert(typeof r.timestamp === 'string' && r.timestamp.length > 0, 'timestamp missing');
});
await record('LIST_COMMANDS returns command map', async () => {
const r = await postJSON(API_URL, { cmd: 'LIST_COMMANDS' });
assert(r && r.commands && typeof r.count === 'number', 'invalid LIST_COMMANDS payload');
});
await record('GET_BEOS with alias email works', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_BEOS', onlyguides: true, what: 'id,name,email' });
assert(Array.isArray(r), 'GET_BEOS did not return array');
if (r.length > 0) {
const it = r[0];
assert('id' in it, 'beo item missing id');
assert('name' in it, 'beo item missing name');
assert(('email' in it) || ('email_1' in it), 'beo item missing email/email_1');
}
});
await record('GET_BEOS with non-existing field falls back', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_BEOS', onlyguides: 'true', what: 'id,name,doesnotexist' });
assert(Array.isArray(r), 'GET_BEOS did not return array on fallback');
});
let sampleDate = null;
await record('GET_TERMINE returns array', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_TERMINE' });
assert(Array.isArray(r), 'GET_TERMINE did not return array');
if (r.length > 0) {
// pick first with possible datum field
const d = r.find(x => typeof x.datum === 'string');
if (d) sampleDate = d.datum;
}
});
if (sampleDate) {
await record('GET_TIME for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_TIME', date: sampleDate });
assert(r && typeof r.time === 'string', 'GET_TIME missing time');
});
let sampleFid = null;
await record('GET_FID for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_FID', datum: sampleDate });
assert(r && ('fid' in r), 'GET_FID missing fid');
sampleFid = r.fid;
});
await record('GET_COUNTS_DATE for sample date', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_COUNTS_DATE', date: sampleDate });
assert(r && typeof r.count === 'number', 'GET_COUNTS_DATE invalid');
});
if (sampleFid) {
await record('GET_COUNTS for sample fid', async () => {
const r = await postJSON(API_URL, { cmd: 'GET_COUNTS', fid: sampleFid });
assert(r && typeof r.count === 'number', 'GET_COUNTS invalid');
});
}
}
if (failures.length) {
console.error(`\n${failures.length} test(s) failed:`);
failures.forEach((f, i) => console.error(` ${i + 1}) ${f.name}: ${f.err && f.err.stack || f.err}`));
process.exit(1);
} else {
console.log('\nAll integration tests passed.');
}
})().catch((e) => { console.error('Fatal error:', e); process.exit(1); });