Finden Sie Unterkünfte in ausgewählten Reisezielen, suchen Sie Flüge für Reisen in die Nähe und in die Ferne und buchen Sie einen Mietwagen, der Ihnen unterwegs Flexibilität bietet. Alles gebündelt in einer übersichtlichen und benutzerfreundlichen Lösung.
BNO Travel bündelt wichtige Bestandteile Ihrer Reise in einer Lösung – abgestimmt auf heutige Anforderungen und künftiges Wachstum.
Finden Sie Ihr nächstes Reiseziel
Von norwegischen Bergen und den Alpen bis zu Sonne, Metropolen und Fernreisen. Entdecken Sie ausgewählte Reiseziele, finden Sie Unterkünfte und planen Sie Ihre Reise mit Flügen und Mietwagen weiter.
Von norwegischen und schwedischen Bergen bis zu den Alpen. Reiseziele für Skifahren, Radfahren, Wandern, Familienurlaub und Naturerlebnisse zu jeder Jahreszeit.
Trysil
Skifahren, Radfahren, Berge und Aktivitäten für die ganze Familie das ganze Jahr über.
Entdecken Sie sorgfältig ausgewählte Unterkünfte in beliebten Regionen und Reisezielen – mit Optionen für kurze Reisen ebenso wie für längere Aufenthalte.
Finden Sie einen Mietwagen, mit dem Sie bequem weiterreisen können – ganz gleich, ob Ihr Ziel eine Stadt, die Berge, die Küste oder eine andere Region ist.
Eine übersichtlichere und flexiblere Art der Reiseplanung
BNO Travel wurde entwickelt, um die Reiseplanung zu vereinfachen. Mit klaren Zugängen zu Unterkünften, Flügen und Mietwagen erhalten Sie eine übersichtlichere Nutzererfahrung – sowohl wenn Sie heute konkret buchen möchten als auch wenn Sie Möglichkeiten für Ihre nächste Reise entdecken wollen.
Flüge und Mietwagen für ein breiteres Reiseangebot
Eine Lösung, die auf weiteres Wachstum ausgelegt ist
BNO Travel App – Behalten Sie Ihre Reise auch unterwegs besser im Blick
BNO Travel wurde für eine optimale Nutzung sowohl auf der Website als auch in der App entwickelt. Beginnen Sie die Planung dort, wo es für Sie am besten passt, und nutzen Sie dieselbe Lösung, um Ihre Reise auch im weiteren Verlauf im Blick zu behalten.
Planen Sie bequem auf Mobilgeräten und am Desktop
Profitieren Sie von einem ganzheitlicheren Reiseerlebnis
Verfolgen Sie Ihre Reise in einer Lösung, die mit Ihren Bedürfnissen wächst
Besitzen Sie eine Hütte, eine Ferienwohnung oder eine andere Unterkunft in einer attraktiven Region? BNO Travel bietet Eigentümern und Vermietern die Möglichkeit, Teil einer professionellen Reiseplattform zu werden, die Wert auf Präsentation, Sichtbarkeit und ein übersichtliches Gästeerlebnis legt.
Wir schaffen die Voraussetzungen dafür, dass Unterkünfte in ausgewählten Reisezielen vertrauenswürdig und professionell präsentiert werden können – als Teil eines ganzheitlichen Reiseerlebnisses. Ziel ist es, relevante Gäste leichter zu erreichen und Ihr Angebot in einem stärkeren digitalen Umfeld zu präsentieren.
';
});
}
appendRichHtml(html);
}
function normalizeIntentText(value) {
return String(value || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/å/g, 'a').replace(/æ/g, 'ae').replace(/ø/g, 'o');
}
function classifyWebTravelIntent(message) {
const value = normalizeIntentText(message);
const categories = [
/overnatting|hytte|leilighet|hotell|hotel|accommodation|lodging/,
/fly|flight|flybillett|avgang/,
/aktivitet|restaurant|reiseforslag|reiseplan|skiferie|ferie/
].filter(function (pattern) { return pattern.test(value); }).length;
if (/reiseforslag|reiseplan|skiferie|familieferie|sett opp|lag et forslag/.test(value) || categories >= 2) return 'trip_planning';
if (/fly|flight|flybillett|avgang/.test(value)) return 'flights';
if (/overnatting|hytte|leilighet|hotell|hotel|accommodation|lodging/.test(value)) return 'accommodation';
if (/restaurant|middag|lunsj|mat/.test(value)) return 'restaurants';
if (/aktivitet|opplevelse|ski|museum/.test(value)) return 'activities';
return 'general';
}
function extractWebDestinationHint(message) {
const value = normalizeIntentText(message);
const matches = [
['portes-du-soleil', ['portes du soleil','morzine','les gets','avoriaz','chatel']],
['tignes-val-disere', ['tignes','val d isere','val disere']],
['chamonix', ['chamonix']], ['verbier', ['verbier']], ['trysil', ['trysil']],
['salen', ['salen','saelen']], ['stranda', ['stranda','strandafjellet']],
['grand-massif', ['grand massif','les carroz']], ['pays-du-mont-blanc', ['pays du mont blanc']],
['3-vallees', ['3 vallees','trois vallees','three valleys']], ['mallorca', ['mallorca','palma']],
['costa-brava', ['costa brava']], ['kaprun', ['kaprun']], ['zell-am-see', ['zell am see']],
['vemdalen', ['vemdalen']]
];
for (let i = 0; i < matches.length; i += 1) {
if (matches[i][1].some(function (term) { return value.indexOf(term) >= 0; })) return matches[i][0];
}
return null;
}
function showInitialHint() {
if (chatEl.children.length) return;
widget.classList.add('is-start');
chatEl.innerHTML =
'
' +
'Testen Sie den BNO KI-Reiseassistenten ' +
'Wählen Sie unten einen Vorschlag aus oder schreiben Sie Ihre Frage in das Feld am unteren Rand.' +
'
';
quickPromptsEl.style.display = '';
}
function clearInitialHint() {
widget.classList.remove('is-start');
const hint = chatEl.querySelector('.bno-helper-empty-state');
if (hint) hint.remove();
}
function rememberDestinationFromResponse(data) {
const candidates = [
data && data.tripProposal && data.tripProposal.destination && data.tripProposal.destination.destinationSlug,
data && data.tripProposal && data.tripProposal.destination && data.tripProposal.destination.area,
data && data.searchContext && data.searchContext.params && data.searchContext.params.area,
data && data.detectedDestination
];
for (const candidate of candidates) {
const value = String(candidate || '').trim();
if (value) {
lastDestinationSlug = value.slice(0, 120);
return;
}
}
}
function removeContactForm() {
const card = chatEl.querySelector('[data-bno-contact-card]');
if (card) card.remove();
contactFormOpen = false;
}
function setSupportUiActive() {
humanRowEl.classList.add('is-active');
humanTitleEl.textContent = 'Sie chatten mit einem Mitglied unseres Teams';
humanHintEl.textContent = 'Direkter Kundenchat mit BNO Travel';
humanButtonEl.classList.add('is-active');
humanButtonEl.innerHTML = '✓Sie chatten mit BNO Travel';
humanButtonEl.disabled = true;
quickPromptsEl.style.display = 'none';
queryEl.placeholder = 'Schreiben Sie eine Nachricht an BNO Travel …';
endSupportBtn.hidden = false;
}
function appendSupportIntro() {
clearInitialHint();
chatEl.insertAdjacentHTML('beforeend',
'
BNO Travel
Sie chatten jetzt mit BNO Travel
Schreiben Sie, wobei Sie Hilfe benötigen. Sobald ein Servicemitarbeiter antwortet, erscheint die Antwort hier.
Wenn Sie die Seite verlassen, bevor wir antworten können, können wir Sie über die angegebene E-Mail-Adresse kontaktieren.
'
);
scrollBottom();
}
function showSupportContactForm() {
if (supportToken) {
enterSupportMode();
return;
}
if (contactFormOpen) {
const existingName = chatEl.querySelector('[data-bno-contact-name]');
if (existingName) existingName.focus();
return;
}
clearInitialHint();
contactFormOpen = true;
quickPromptsEl.style.display = 'none';
setMainInputEnabled(false);
setStatus('Geben Sie Ihren Namen und Ihre E-Mail-Adresse ein, um einen Chat mit BNO Travel zu starten.');
chatEl.insertAdjacentHTML('beforeend',
'
' +
'
' +
'Mit einem Mitglied unseres Teams chatten' +
'
Geben Sie Ihren Namen und Ihre E-Mail-Adresse an. So können wir Sie kontaktieren, falls Sie die Website verlassen, bevor wir antworten können.
' +
'' +
'
' +
'
'
);
scrollBottom();
setTimeout(function () {
const nameInput = chatEl.querySelector('[data-bno-contact-name]');
const emailInput = chatEl.querySelector('[data-bno-contact-email]');
if (nameInput && !nameInput.value) nameInput.focus();
else if (emailInput) emailInput.focus();
}, 50);
}
function cancelSupportContactForm() {
removeContactForm();
if (!supportMode) quickPromptsEl.style.display = '';
setMainInputEnabled(true);
setStatus('');
if (!chatEl.children.length) showInitialHint();
queryEl.focus();
}
function enterSupportMode() {
removeContactForm();
if (!supportMode) appendSupportIntro();
supportMode = true;
setSupportUiActive();
setMainInputEnabled(true);
setStatus('Direkter Kundenchat mit BNO Travel', 'success');
if (supportToken) startSupportPolling();
queryEl.focus();
}
function submitSupportContactForm(form) {
const nameInput = form.querySelector('[data-bno-contact-name]');
const emailInput = form.querySelector('[data-bno-contact-email]');
const errorEl = form.querySelector('[data-bno-contact-error]');
const name = String(nameInput && nameInput.value || '').trim();
const email = String(emailInput && emailInput.value || '').trim().toLowerCase();
if (!name) {
errorEl.textContent = 'Geben Sie Ihren Namen ein.';
if (nameInput) nameInput.focus();
return;
}
if (!isValidEmail(email)) {
errorEl.textContent = 'Geben Sie eine gültige E-Mail-Adresse ein.';
if (emailInput) emailInput.focus();
return;
}
errorEl.textContent = '';
saveSupportProfile({ name: name, email: email });
enterSupportMode();
}
function resetSupportUi() {
supportMode = false;
contactFormOpen = false;
supportToken = '';
supportProfile = { name: '', email: '' };
sessionStorage.removeItem(STORAGE_KEY);
sessionStorage.removeItem(PROFILE_KEY);
seenSupportMessageIds.clear();
if (supportPollTimer) clearInterval(supportPollTimer);
supportPollTimer = null;
setSupportUiIdle();
setMainInputEnabled(true);
setStatus('');
}
function resetWholeConversation() {
resetSupportUi();
travelHistory = [];
lastDestinationSlug = '';
lastPackageOptions = [];
lastPackageVisibleCount = 0;
chatEl.innerHTML = '';
queryEl.value = '';
resizeInput();
showInitialHint();
scrollBottom();
}
async function endSupportConversation() {
const tokenToClose = supportToken;
endSupportBtn.disabled = true;
setStatus('Chat wird beendet …');
try {
if (tokenToClose) {
const response = await fetch(API_BASE + SUPPORT_ENDPOINT + '/conversations/' + encodeURIComponent(tokenToClose), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
cache: 'no-store',
body: JSON.stringify({ status: 'closed' })
});
const data = await response.json().catch(function () { return null; });
if (!response.ok || !data || !data.ok) {
throw new Error(data && data.error || 'support_close_failed');
}
}
resetWholeConversation();
setStatus('Der Chat wurde beendet. Sie können eine neue Unterhaltung starten.', 'success');
} catch (error) {
console.error('[BNO SUPPORT CLOSE]', error);
setStatus('Der Chat konnte gerade nicht beendet werden. Bitte versuchen Sie es erneut.', 'error');
} finally {
endSupportBtn.disabled = false;
}
}
async function sendTravelHelper(message) {
clearInitialHint();
quickPromptsEl.style.display = 'none';
appendUserMessage(message);
setStatus('Der BNO KI-Reiseassistent denkt nach …');
submitEl.disabled = true;
const language = getLanguage();
const nextHistory = travelHistory.concat([{ role: 'user', text: message }]).slice(-16);
try {
const response = await fetch(API_BASE + TRAVEL_HELPER_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
cache: 'no-store',
body: JSON.stringify({
message: message,
lang: language,
replyLanguage: language,
detectedIntent: classifyWebTravelIntent(message),
detectedDestination: extractWebDestinationHint(message),
history: nextHistory,
helperContext: {
replyLanguage: language,
detectedIntent: classifyWebTravelIntent(message),
detectedDestination: extractWebDestinationHint(message),
isTripPlanning: classifyWebTravelIntent(message) === 'trip_planning',
source: 'bno-travel-web',
pageUrl: window.location.href
}
})
});
const data = await response.json().catch(function () { return null; });
if (!response.ok || !data || !data.reply) {
throw new Error(data && data.error || 'travel_helper_failed');
}
rememberDestinationFromResponse(data);
const reply = String(data.reply);
const packageOptions = data.packageAction && Array.isArray(data.packageAction.options) ? data.packageAction.options : [];
travelHistory = nextHistory.concat([{ role: 'assistant', text: reply }]).slice(-16);
if (packageOptions.length) {
lastPackageOptions = packageOptions.slice();
lastPackageVisibleCount = 0;
const narrativeHtml = buildPackageNarrativeHtml(packageOptions);
if (narrativeHtml) appendAssistantRichMessage(narrativeHtml, 'BNO KI-Reiseassistent');
else appendAssistantMessage(reply, 'BNO KI-Reiseassistent');
} else {
appendAssistantMessage(reply, 'BNO KI-Reiseassistent');
}
await renderTravelResponseExtras(data);
setStatus('');
} catch (error) {
console.error('[BNO WEB HELPER]', error);
appendAssistantMessage('Entschuldigung, der BNO KI-Reiseassistent ist derzeit nicht verfügbar. Sie können weiterhin „Mit einem Mitglied unseres Teams chatten“ auswählen, um BNO Travel zu kontaktieren.', 'BNO KI-Reiseassistent');
setStatus('Es konnte keine Antwort vom BNO Reiseassistenten abgerufen werden.', 'error');
} finally {
submitEl.disabled = false;
}
}
async function createSupportConversation(message) {
if (!supportProfile.name || !isValidEmail(supportProfile.email)) {
throw new Error('support_profile_required');
}
const language = getLanguage();
const response = await fetch(API_BASE + SUPPORT_ENDPOINT + '/conversations', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
cache: 'no-store',
body: JSON.stringify({
customerName: supportProfile.name,
customerEmail: supportProfile.email,
language: language,
destinationSlug: lastDestinationSlug || null,
pageUrl: window.location.href,
initialMessage: message,
travelHelperContext: {
history: travelHistory.slice(-12),
source: 'bno-travel-web',
pageUrl: window.location.href
}
})
});
const data = await response.json().catch(function () { return null; });
if (!response.ok || !data || !data.ok || !data.data || !data.data.publicToken) {
throw new Error(data && data.error || 'support_create_failed');
}
supportToken = String(data.data.publicToken);
sessionStorage.setItem(STORAGE_KEY, supportToken);
startSupportPolling();
}
async function sendSupportMessage(message) {
clearInitialHint();
appendUserMessage(message);
setStatus('Wird an BNO Travel gesendet …');
submitEl.disabled = true;
try {
if (!supportToken) {
await createSupportConversation(message);
} else {
const response = await fetch(API_BASE + SUPPORT_ENDPOINT + '/conversations/' + encodeURIComponent(supportToken) + '/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
cache: 'no-store',
body: JSON.stringify({ body: message })
});
const data = await response.json().catch(function () { return null; });
if (!response.ok || !data || !data.ok) throw new Error(data && data.error || 'support_message_failed');
}
setStatus('Ihre Nachricht wurde an BNO Travel gesendet.', 'success');
await pollSupportMessages();
} catch (error) {
console.error('[BNO SUPPORT CHAT]', error);
if (String(error && error.message || '') === 'support_profile_required') {
supportMode = false;
setSupportUiIdle();
showSupportContactForm();
setStatus('Name und E-Mail-Adresse müssen ausgefüllt werden, bevor die Nachricht gesendet werden kann.', 'error');
} else {
setStatus('Die Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es erneut.', 'error');
}
} finally {
if (!contactFormOpen) submitEl.disabled = false;
}
}
async function pollSupportMessages() {
if (!supportToken) return;
try {
const response = await fetch(API_BASE + SUPPORT_ENDPOINT + '/conversations/' + encodeURIComponent(supportToken) + '/messages', {
cache: 'no-store'
});
const data = await response.json().catch(function () { return null; });
if (!response.ok || !data || !data.ok) return;
if (data.data && data.data.conversation && data.data.conversation.status === 'closed') {
resetWholeConversation();
setStatus('Dieser Kundenchat wurde beendet. Sie können eine neue Unterhaltung starten.', 'success');
return;
}
const messages = Array.isArray(data.data && data.data.messages) ? data.data.messages : [];
const staffMessages = messages.filter(function (message) { return message.sender_type === 'staff'; });
staffMessages.forEach(function (message) {
const messageId = String(message.id || '');
if (!messageId || seenSupportMessageIds.has(messageId)) return;
const staffName = firstText(message.sender_name, message.staff_name, message.display_name);
appendAssistantMessage(String(message.body || ''), staffName ? staffName + ', BNO Travel' : 'BNO Travel');
seenSupportMessageIds.add(messageId);
});
} catch (error) {
console.warn('[BNO SUPPORT POLL]', error);
}
}
function startSupportPolling() {
if (supportPollTimer) clearInterval(supportPollTimer);
pollSupportMessages();
supportPollTimer = window.setInterval(pollSupportMessages, 8000);
}
async function handleSend(messageOverride) {
if (contactFormOpen) return;
const message = String(messageOverride || queryEl.value || '').trim();
if (!message) {
setStatus('Schreiben Sie zuerst eine Nachricht.');
return;
}
queryEl.value = '';
resizeInput();
if (!supportMode) {
const selectedPackageIndex = extractPackageChoiceIndex(message);
if (selectedPackageIndex) {
appendUserMessage(message);
openPackageOption(selectedPackageIndex);
return;
}
if (isMorePackageRequest(message)) {
appendUserMessage(message);
await showNextPackageOptions();
return;
}
}
if (supportMode) await sendSupportMessage(message);
else await sendTravelHelper(message);
}
launcher.addEventListener('click', openHelper);
closeBtn.addEventListener('click', closeHelper);
overlay.addEventListener('click', closeHelper);
humanButtonEl.addEventListener('click', function () { showSupportContactForm(); });
endSupportBtn.addEventListener('click', function () { endSupportConversation(); });
submitEl.addEventListener('click', function () { handleSend(); });
queryEl.addEventListener('input', resizeInput);
queryEl.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
handleSend();
}
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && widget.classList.contains('is-open')) closeHelper();
});
quickPromptsEl.addEventListener('click', function (event) {
const button = event.target.closest('button[data-prompt]');
if (!button || supportMode) return;
handleSend(button.getAttribute('data-prompt') || '');
});
chatEl.addEventListener('click', function (event) {
const packageButton = event.target.closest('[data-bno-package-option-index]');
if (packageButton && !supportMode) {
event.preventDefault();
const index = Number(packageButton.getAttribute('data-bno-package-option-index'));
openPackageOption(index);
return;
}
const moreButton = event.target.closest('[data-bno-show-more-package]');
if (moreButton && !supportMode) {
event.preventDefault();
moreButton.disabled = true;
showNextPackageOptions();
return;
}
const cancel = event.target.closest('[data-bno-contact-cancel]');
if (cancel) {
event.preventDefault();
cancelSupportContactForm();
}
});
chatEl.addEventListener('submit', function (event) {
const form = event.target.closest('[data-bno-contact-form]');
if (!form) return;
event.preventDefault();
submitSupportContactForm(form);
});
showInitialHint();
// Give the closed BNO Reisehjelper launcher one subtle attention cue on page load.
window.setTimeout(startLauncherAttention, 900);
if (supportToken) {
supportMode = true;
clearInitialHint();
appendSupportIntro();
setSupportUiActive();
setStatus('Direkter Kundenchat mit BNO Travel', 'success');
startSupportPolling();
}
})();