template_artikel Archive - Seite 19 von 39 - andererseits.org
Zum Inhalt springen
Wie Menschen mit Behinderungen verfolgt wurden.
Frauen mit Behinderungen erleben öfter Gewalt
als Frauen ohne Behinderungen.
Kranken-Kassen wollen oft wichtige Dinge nicht bezahlen.
Darum müssen viele Menschen um die Dinge kämpfen.
Charlie Fitz ist eine Künstlerin.Sie ist auf den Fotos zu sehen.Charlie Fitz hat eine seltene Krankheit. Ihr Arzt musste eine Operation machen, die er noch nie davor gemacht hat. Diese Fotos wurden aufgenommen,während sich Charlie Fitz von dieser Operation erholt. Sie sind unterstützte Fotos.Das heißt:Charlie Fitz hat die Fotoszusammen mit Oscar Vinter gemacht.Oscar Vinter ist […]
Hanna Gugler sagt: Wenn alle wie ich faulenzen würden,
dann wäre das schon gut.
Politik geht alle an. Aber wird Politik für alle gemacht? Wir haben geschaut, was in den Partei-Programmen zur deutschen Bundes-Tags-Wahl zu Inklusion steht.
Unser Autor erzählt vom ersten Tag des russischen Angriffs auf die Ukraine. Was bedeutet es, wenn um vier Uhr morgens Bomben einschlagen und man im Rollstuhl im zwölften Stock sitzt?
Menschen mit Behinderung müssen in ihrem Alltag viele Hürden überwinden. Besonders kompliziert wird es beim Dating. Wir haben mit drei Menschen mit Behinderungen über die Liebe gesprochen.
Hanna schreibt bei andererseits darüber, wie die Welt sein könnte.
Ramona ist unsere Social-Media-Chefin und arbeitet in der Redaktion. Gemeinsam machen die beiden die beste Koch-Show der Welt.
Aus dem Bauch heraus: Die FPÖ ist ausländer-feindlich
Aus der Welt: Klettern kann für alle sein
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt.
Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Zustimmung verwalten
. No dependencies.
* Keeps WordPress/MediaElement playback; uses native inputs for accessibility.
* Optional on : data-audio-title="Titel des Artikels".
*/
(() => {
"use strict";
if (window.andererseitsAudio) {
window.andererseitsAudio.scan();
return;
}
let nextId = 0;
const enhanced = new WeakSet();
const players = new Set();
const clock = (value) => {
const seconds = Math.max(0, Math.floor(value || 0));
const parts = [Math.floor(seconds / 60) % 60, seconds % 60];
if (seconds >= 3600) parts.unshift(Math.floor(seconds / 3600));
return parts.map((part) => String(part).padStart(2, "0")).join(":");
};
const spokenTime = (value) => {
const seconds = Math.max(0, Math.floor(value || 0));
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return [
hours && `${hours} ${hours === 1 ? "Stunde" : "Stunden"}`,
minutes && `${minutes} ${minutes === 1 ? "Minute" : "Minuten"}`,
`${seconds % 60} ${seconds % 60 === 1 ? "Sekunde" : "Sekunden"}`,
]
.filter(Boolean)
.join(", ");
};
function enhance(audio) {
const root = audio.closest(".mejs-container.mejs-audio.wp-audio-shortcode");
const player = audio.player;
if (!root || !player || !player.media || enhanced.has(root)) return;
const controls = root.querySelector(".mejs-controls");
const rail = root.querySelector(".mejs-time-rail");
const play = root.querySelector(".mejs-playpause-button > button");
let muteBox = root.querySelector(".mejs-volume-button");
if (!controls || !rail || !play) return;
enhanced.add(root);
const id = `ae-audio-${++nextId}`;
if (!audio.id) audio.id = `${id}-media`;
const precedingWidget =
root.closest(".elementor-widget")?.previousElementSibling;
const nearbyTitle =
precedingWidget?.matches(
".elementor-widget-text-editor, .elementor-widget-heading",
) && precedingWidget.textContent.trim().length <= 120
? precedingWidget.textContent.trim()
: "";
const explicitTitle =
audio.dataset.audioTitle ||
root.closest("[data-audio-title]")?.dataset.audioTitle ||
audio.getAttribute("aria-label");
const pageTitle = (
root.closest("article")?.querySelector("h1") ||
document.querySelector("h1")
)?.textContent.trim();
const title =
explicitTitle ||
nearbyTitle ||
(pageTitle ? `${pageTitle}, Audio ${nextId}` : "");
root.setAttribute("role", "group");
root.setAttribute(
"aria-label",
title ? `Audio: ${title}` : `Audioplayer ${nextId}`,
);
root.removeAttribute("tabindex");
const oldTitle = root.previousElementSibling;
if (oldTitle?.classList.contains("mejs-offscreen")) oldTitle.hidden = true;
// MediaElement's global shortcuts interfere with buttons/selects and
// restart paused audio when seeking. Native controls own their keys.
player.options.enableKeyboard = false;
player.options.autoRewind = false;
player.options.playText = "Wiedergeben";
player.options.pauseText = "Pausieren";
// −10 / +10 flank Play, so visual and tab order match.
const skipBack = document.createElement("div");
skipBack.className = "ae-audio-skips";
skipBack.innerHTML = `10 `;
const skipForward = document.createElement("div");
skipForward.className = "ae-audio-skips";
skipForward.innerHTML = `10 `;
const tools = document.createElement("div");
tools.className = "ae-audio-tools";
tools.innerHTML = `
1×
Lautstärke
`;
const status = document.createElement("div");
status.className = "ae-audio-status ae-audio-sr";
status.setAttribute("role", "status");
status.setAttribute("aria-live", "polite");
status.setAttribute("aria-atomic", "true");
const error = document.createElement("p");
error.className = "ae-audio-error";
error.hidden = true;
const help = document.createElement("span");
help.id = `${id}-help`;
help.className = "ae-audio-sr";
help.textContent =
"Pfeiltasten: 10 Sekunden. Bild auf oder ab: 30 Sekunden. Pos1: Anfang. Ende: Schluss.";
const seek = document.createElement("input");
seek.type = "range";
seek.className = "ae-audio-range ae-audio-seek";
seek.min = "0";
seek.max = "0";
seek.step = "1";
seek.value = "0";
seek.setAttribute("aria-label", "Wiedergabeposition");
seek.setAttribute("aria-describedby", help.id);
const tooltip = document.createElement("span");
tooltip.className = "ae-audio-tooltip";
tooltip.setAttribute("aria-hidden", "true");
const originalSlider = root.querySelector(".mejs-time-total");
originalSlider?.setAttribute("aria-hidden", "true");
originalSlider?.setAttribute("tabindex", "-1");
rail.append(seek, tooltip, help);
const inner = root.querySelector(".mejs-inner");
play.parentElement.before(skipBack);
play.parentElement.after(skipForward);
controls.append(tools);
inner.append(error, status);
if (!muteBox) {
muteBox = document.createElement("div");
muteBox.className = "mejs-button mejs-volume-button mejs-mute";
muteBox.innerHTML =
' ';
}
const volumeBox = tools.querySelector(".ae-audio-volume");
volumeBox.prepend(muteBox);
const mute = muteBox?.querySelector("button");
const volume = tools.querySelector("input");
const speed = tools.querySelector(".ae-audio-rate");
const skipButtons = [skipBack, skipForward].map((box) => box.firstChild);
let previousVolume = audio.volume || 0.8;
root.querySelectorAll("button, input, select").forEach((control) => {
control.setAttribute("aria-controls", audio.id);
});
// Retain tab order and browser/AT default behavior. No single-letter
// shortcuts, application mode, focus trapping or document key listeners.
let announcementTimer;
function announce(message) {
clearTimeout(announcementTimer);
status.textContent = "";
announcementTimer = setTimeout(() => {
if (root.isConnected) status.textContent = message;
}, 60);
}
const duration = () =>
Number.isFinite(audio.duration) && audio.duration > 0
? audio.duration
: 0;
function loadMetadata() {
if (audio.preload === "none" && !audio.readyState && !audio.error) {
audio.preload = "metadata";
audio.load();
}
}
// Respect lazy loading: only request metadata for the player a person
// enters, rather than downloading every clip on a page with many players.
root.addEventListener("focusin", loadMetadata);
root.addEventListener("pointerenter", loadMetadata, { once: true });
// Handle rejected play promises (network errors, browser restrictions)
// instead of letting MediaElement silently discard them.
play.addEventListener("click", (event) => {
event.stopPropagation();
if (!audio.paused && !audio.ended) {
audio.pause();
return;
}
if (audio.ended) audio.currentTime = 0;
const attempt = audio.play();
attempt?.catch((reason) => {
if (reason.name === "AbortError") return;
error.hidden = false;
error.textContent =
"Wiedergabe nicht möglich. Bitte erneut versuchen oder die Seite neu laden.";
announce(error.textContent);
syncButtons();
});
});
function paintRange(input, value, maximum) {
input.style.setProperty(
"--ae-fill",
`${maximum ? (value / maximum) * 100 : 0}%`,
);
}
function updateTime() {
const maximum = duration();
const current = Math.min(maximum, Math.max(0, audio.currentTime || 0));
seek.max = String(maximum);
seek.value = String(current);
seek.disabled = !maximum || !!audio.error;
seek.setAttribute(
"aria-valuetext",
maximum
? `${spokenTime(current)} von ${spokenTime(maximum)}`
: "Dauer noch nicht verfügbar",
);
paintRange(seek, current, maximum);
tooltip.textContent = clock(current);
skipButtons.forEach((button) =>
button.setAttribute("aria-disabled", String(!maximum || !!audio.error)),
);
}
function syncButtons() {
const playLabel =
audio.paused || audio.ended ? "Wiedergeben" : "Pausieren";
if (play.getAttribute("aria-label") !== playLabel)
play.setAttribute("aria-label", playLabel);
if (play.title !== playLabel) play.title = playLabel;
if (mute) {
const silent = audio.muted || audio.volume === 0;
const label = silent ? "Ton einschalten" : "Stummschalten";
if (mute.getAttribute("aria-label") !== label)
mute.setAttribute("aria-label", label);
if (mute.title !== label) mute.title = label;
muteBox.classList.toggle("mejs-mute", !silent);
muteBox.classList.toggle("mejs-unmute", silent);
}
}
function syncVolume() {
if (audio.volume > 0) previousVolume = audio.volume;
const value = audio.muted ? 0 : Math.round(audio.volume * 100);
volume.value = String(value);
volume.setAttribute(
"aria-valuetext",
`${value} Prozent${audio.muted ? ", stummgeschaltet" : ""}`,
);
paintRange(volume, value, 100);
syncButtons();
}
function seekTo(value, announcePosition = false) {
if (!duration() || audio.error) {
loadMetadata();
if (announcePosition)
announce(
audio.error
? "Audio ist nicht verfügbar."
: "Audio wird geladen. Bitte kurz warten.",
);
return;
}
try {
audio.currentTime = Math.max(0, Math.min(duration(), value));
updateTime();
if (announcePosition)
announce(
`Position: ${spokenTime(audio.currentTime)} von ${spokenTime(duration())}.`,
);
} catch {
announce("Diese Position ist noch nicht verfügbar.");
}
}
skipButtons.forEach((button) =>
button.addEventListener("click", () => {
seekTo(audio.currentTime + Number(button.dataset.seconds), true);
}),
);
seek.addEventListener("input", () => seekTo(Number(seek.value)));
seek.addEventListener("keydown", (event) => {
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
return;
const offsets = {
ArrowLeft: -10,
ArrowDown: -10,
ArrowRight: 10,
ArrowUp: 10,
PageDown: -30,
PageUp: 30,
};
const target =
event.key === "Home"
? 0
: event.key === "End"
? duration()
: event.key in offsets
? audio.currentTime + offsets[event.key]
: null;
if (target === null) return;
event.preventDefault();
event.stopPropagation();
seekTo(target);
});
seek.addEventListener("pointermove", (event) => {
const box = seek.getBoundingClientRect();
const fraction = Math.max(
0,
Math.min(1, (event.clientX - box.left) / box.width),
);
tooltip.textContent = clock(fraction * duration());
});
volume.addEventListener("input", () => {
const value = Number(volume.value) / 100;
audio.volume = value;
audio.muted = value === 0;
if (Math.abs(audio.volume - value) > 0.01) {
announce(
"Bitte die Lautstärketasten des Geräts verwenden. Dieser Browser erlaubt keinen Lautstärkeregler.",
);
}
syncVolume();
});
mute.addEventListener(
"click",
(event) => {
event.stopImmediatePropagation();
if (audio.muted || audio.volume === 0) {
if (audio.volume === 0) audio.volume = previousVolume;
audio.muted = false;
} else audio.muted = true;
syncVolume();
},
true,
);
// Native range supplies arrows/Home/End, including AT increment actions.
// Escape hides the slider shown on hover/focus (WCAG 1.4.13) and returns
// focus to the mute button; it reappears once focus or pointer re-enter.
function dismissVolume(event) {
if (event.key !== "Escape" || getComputedStyle(volume).opacity === "0")
return;
event.preventDefault();
event.stopPropagation();
volumeBox.classList.add("ae-audio-volume-dismissed");
if (document.activeElement === volume) mute.focus();
}
const restoreVolume = () =>
volumeBox.classList.remove("ae-audio-volume-dismissed");
volumeBox.addEventListener("keydown", dismissVolume);
// While hovered, focus may be elsewhere on the page.
volumeBox.addEventListener("pointerenter", () =>
document.addEventListener("keydown", dismissVolume),
);
volumeBox.addEventListener("pointerleave", () => {
document.removeEventListener("keydown", dismissVolume);
restoreVolume();
});
volume.addEventListener("focus", restoreVolume);
volumeBox.addEventListener("focusout", (event) => {
if (!volumeBox.contains(event.relatedTarget)) restoreVolume();
});
// One button cycles the tempo; the visible value is part of its name.
const rates = [1, 1.25, 1.5, 2, 0.5, 0.75];
const rateText = (rate) => `${String(rate).replace(".", ",")}×`;
function syncRate() {
speed.textContent = rateText(audio.playbackRate);
speed.setAttribute(
"aria-label",
`Abspielgeschwindigkeit ${rateText(audio.playbackRate)}`,
);
}
speed.addEventListener("click", () => {
const next =
rates[(rates.indexOf(audio.playbackRate) + 1) % rates.length];
try {
audio.playbackRate = next;
announce(
`Abspielgeschwindigkeit: ${String(audio.playbackRate).replace(".", ",")} mal.`,
);
} catch {
announce(
"Diese Abspielgeschwindigkeit wird vom Browser nicht unterstützt.",
);
}
syncRate();
});
audio.addEventListener("ratechange", syncRate);
[
"timeupdate",
"durationchange",
"loadedmetadata",
"emptied",
"seeked",
].forEach((name) => audio.addEventListener(name, updateTime));
["play", "playing", "pause", "ended", "loadedmetadata"].forEach((name) =>
audio.addEventListener(name, syncButtons),
);
// MediaElement can overwrite localized labels in a later renderer event.
// Observe only those two attributes, and write only when different.
const labelObserver = new MutationObserver(syncButtons);
[play, mute].forEach((button) =>
labelObserver.observe(button, {
attributes: true,
attributeFilter: ["aria-label", "title"],
}),
);
audio.addEventListener("volumechange", syncVolume);
audio.addEventListener("play", () => {
error.hidden = true;
for (const other of players) {
if (!other.isConnected) players.delete(other);
else if (other !== audio && !other.paused) other.pause();
}
});
audio.addEventListener("ended", () => announce("Wiedergabe beendet."));
const showError = () => {
error.hidden = false;
error.textContent =
"Audio konnte nicht geladen werden. Bitte die Seite neu laden oder es später erneut versuchen.";
announce(error.textContent);
updateTime();
};
audio.addEventListener("error", showError);
audio.addEventListener("loadstart", () => {
error.hidden = true;
});
players.add(audio);
root.classList.add("ae-audio-enhanced");
updateTime();
syncVolume();
syncRate();
if (audio.error) showError();
}
function scan() {
document.querySelectorAll("audio.wp-audio-shortcode").forEach(enhance);
}
window.andererseitsAudio = { scan };
// WordPress and Elementor may initialize/add players after this snippet.
let queued = false;
const observer = new MutationObserver((records) => {
if (
queued ||
!records.some((record) =>
[...record.addedNodes].some(
(node) =>
node.nodeType === 1 &&
(node.matches("audio, .mejs-controls") ||
node.querySelector("audio, .mejs-controls")),
),
)
)
return;
queued = true;
requestAnimationFrame(() => {
queued = false;
scan();
});
});
function start() {
scan();
observer.observe(document.body, { childList: true, subtree: true });
}
if (document.readyState === "loading")
document.addEventListener("DOMContentLoaded", start, { once: true });
else start();
})();
/* andererseits Audio-Player Erweiterung – ENDE */