/* Dieses Script wurde geschrieben von Mitja Stachowiak (www.mitjastachowiak.de) Es benoetigt ausserdem frameCommunicator.js. Dokumentation unter http://www.mitjastachowiak.de/code/js/dynamicIFrame */ function getTickCount () { // returns the milliseconds since beginning of the current year var T = new Date(); return T.getMilliseconds() + T.getSeconds() * 1000 + T.getMinutes() * 60000 + T.getHours() * 3600000 + T.getDay() * 86400000; } function createOverscrollListener (doc, wait, callback) { var o = new Object(); o.doc = doc; o.lastScroll = 0; o.lastTry = 0; o.lastInnerWidth = window.innerWidth; o.wait = wait / 2; o.callback = callback; o.scrollFkt = function () { o.lastScroll = getTickCount(); } o.scrollEventFkt = function (ev) { if (getTickCount() < o.lastScroll + o.wait) { o.lastTry = 0; return; } if (o.lastTry == 0) o.lastTry = getTickCount(); if (getTickCount() < o.lastTry + o.wait) { o.lastInnerWidth = window.innerWidth; return; } if (o.lastInnerWidth != window.innerWidth) { o.lastInnerWidth = window.innerWidth; return; } o.lastTry = 0; if (!ev) ev = window.event; var dir = ev.wheelDelta || -ev.detail; if (!callback) return; if (dir) { o.callback(dir < 0); return; } if (window.pageYOffset > 0) { o.callback(true); return; } if (ev.keyCode) { o.callback(ev.keyCode == 40); return; } } o.free = function () { o.doc.removeEventListener('scroll', o.scrollFkt, false); o.doc.removeEventListener('DOMMouseScroll', o.scrollEventFkt, false); o.doc.removeEventListener('mousewheel', o.scrollEventFkt, false); o.doc.removeEventListener('keypress', o.scrollEventFkt, false); o.callback = null; o.doc = null; o = null; } // EventListener setzen var d = o.doc; var _d = null; while (d && d != _d) { try { _d = d; d.addEventListener('scroll', o.scrollFkt, false); d = d.defaultView.parent.document; } catch (e) { break; } } o.doc.addEventListener('DOMMouseScroll', o.scrollEventFkt, false); o.doc.addEventListener('mousewheel', o.scrollEventFkt, false); o.doc.addEventListener('keypress', o.scrollEventFkt, false); return o; } /* Dies sind zwei standard-Effekte. Diese koennen Sie als Vorlage fuer eigene Effekte verwenden Parameter: el : iframe-Element s : Wert zwischen 0 und 1; Ausnahme -1 (0 = Effekt beginnt, 0.5 = Effekt ist bei der Haelfte, 1 = Effekt ist fertig, -1 = saemtliche von diesem Effekt benutzten css-Eigenschaften zuruecksetzen) blendIn : Wenn true, soll das iframe eingeblendet werden, ansonsten ausgeblendet */ function EffectBlend (el, s, blendIn) { if (s < 0) { el.style.opacity = 1; return; } if (!blendIn) s = 1-s; el.style.opacity = s; } function EffectBlendAndSlide (el, s, blendIn) { if (s < 0) { el.style.marginTop = '0px'; el.style.opacity = 1; return; } if (!blendIn) s = 1-s; el.style.opacity = s; el.style.marginTop = -100 * (1-s) * (1-s) + 'px'; } function EffectAppendAndSlide (el1, el2, s, dir) { if (s < 0) { el1.style.marginTop = '0px'; el2.style.marginTop = '0px'; el1.parentElement.style.overflow = 'visible'; return; } el1.parentElement.style.overflow = 'hidden'; if (dir) { el1.style.marginTop = -el1.offsetHeight * (1-s); el2.style.marginTop = el1.offsetHeight * (s); } else { el2.style.marginTop = -el2.offsetHeight * (s); el1.style.marginTop = el2.offsetHeight * (1-s); } } /* Dieses Objekt darf nur einmal in einem HTML-Dokument erstellt werden und erlaubt, das Laden von Seiten in einem iframe mit Effekt zu versehen. Parameter: FrameBox : ein DIV-Element, das genau 2 iframes enthaellt. Jedes IFrame benoetigt ein eindeutriges name-Attribut. Nur so kann die Kompatibilitaet mit Links ohne JavaScript gewaehrleistet werden (). Eigenschaften und Funktionen: whitelist : Array aus RegExp-Objekten. Alle Seiten, die im Frame geladen werden sollen, muessen entweder von der gleichen Domain sein, oder auf die Whitelist passen addURLToWhitelist : Wandelt die uebergebene URL in ein RegExp-Objekt um und fuegt sie der whitelist hinzu. In der URL sind *-Zeichen erlaubt. Innerhalb des Hostname-Bereiches haben diese jedoch spezielle Aufgaben und es kann nicht jede beliebige Zeichenfolge fuer ein * stehen. differentQueryDifferentSites : Boolean - wenn true, dann werden Menuelinks nur hervorgehoen, wenn auch das QUerystring des Menuelinks und der Seite im Frame gleich ist. whitelist : Array aus Strigs (Darf Platzhalter (*) enthalten). Nur Seiten, die in dieses Profil passen, duerfen im Frame geladen werden. Seiten von der eigenen Domain duerfen immer geladen werden. maxWaitForSite : diese Zeit (ms) wird maximal gewartet, bis die neue Seite eingeblendet wird. Ist die Seite bis dahin nicht vollstaendig geladen, kann der Besucher beobachten, wie sie im iframe fertig laedt. blendOutTime : Dauer des Ausblendens (ms) blendInDelay : Verzoegerung - so viele Millisekunden nach Beginn des Ausblendens beginnt das Einblenden blendInTime : Dauer des Einblendens (ms) blendInEffect : Uebergeben Sie hier die Effekt-Funktion fuer das Einblenden blendOutEffect : Uebergeben Sie hier die Effekt-Funktion fuer das Ausblenden function loadSite : Rufen Sie diese Funktion mit einer URL auf, um diese URL mit Effekt in den iframe zu laden (Die Seite wird nur geladen, wenn sie von der gleichen Domain ist oder auf die Whitelist passt) function scrollTo : uebergeben werden kann eine y-Koordinate aus Sicht des aktuellen Iframes oder der Name eines Ankers innerhalb des aktuellen Iframes. ScrollTo scrollt dann zu diesem Ziel. Ein zweiter Parameter darf nur vom Objekt selbst uebergeben werden. autoFillMenu : Falls die Menu-Links ein Inhaltsverzeichnis darstellen, kann fuer autoFillMenu eine Zahl eingesetzt werden, bis zu der H*-Elemente der Frame-Seite gescant und automatisch im Inhaltsverzeichnis ergaenzt werden. Ein Wert von 3 z.B. bedeutet, dass fuer alle H1, H2 und H3-Elemente automatisch Links im Inhaltsverzeichnis beim Laden der Seite angelegt werden. */ function DynamicIFrame (FrameBox, debugLogging=false) { with (this) { // private var box = FrameBox; var frames = FrameBox.getElementsByTagName('iframe'); var current = 0; var next = 1; var communicators = new Array(); var aTarget, aTarget2; var isAutoFrameHeight = true; var menuLinks = new Array(); var printLinks = new Array(); var blendInfo = new Object(); var scrollInfo = new Object(); var autoHeightEl = new Array(); // Alle Elemente, deren NoJsHeight-Classname gesetzt/entfernt wird var loadWithoutEffect = false; var startPage = ''; var incompatibleBrowser = false; var domWasReady = false; var selfHostname; var currentSite = ''; var loadingSiteAnchor = ''; // Anker der zu ladenden Seite aus URL, wird gespeichert fuer scroll-Effekt var currentSiteAnchors = new Array(); // Array aus allen Ueberschrifen-Ankern der aktuellen Seite (kann nach absOffsetTop der Anker sortiert werden) var anchorPos = 0; // index in currentSiteAnchors des gerade aktiven Ankers var overscrollListener = null; var currentHirachy = null; var oldQuery = ''; var scriptLoadingPage = ''; // public this.differentQueryDifferentSites = false; this.whitelist = new Array(); this.maxWaitForSite = 1000; // Nach dieser Zeit wird die Seite anzegeigt, auch wenn noch nicht vollst. geladen this.blendOutTime = 0; this.blendInDelay = 0; this.blendInTime = 0; this.scrollTime = 500; this.blendInEffect = null; this.blendOutEffect = null; this.browseEffect = EffectAppendAndSlide; this.browseDuration = 2000; this.scrollFunction = function (s) { return (-2*s+3)*s*s; } this.browseScrollFunction = function (s) { return s; } this.autoFillMenu = 0; // Bis zu dieser Zahl * werden H*-Elemente im Menue ergaenzt this.autoChangeAnchor = false; this.nummerateMenu = false; this.onSiteLoaded = null; this.onLinkActivate = null; this.onMenuChange = null; this.onTitleChange = null; this.onContentResize = null; var _this = this; var onEvent = /* private */ function (el, event, fkt, param, behavior) { // provides easy eventListeners. el: Element to set the listener; event: string-name of the event(without "on"); fkt: a Function to call ift the event occours, fkt is called in the scope of _this; param: any parameter, that is given to fkt; behavior: flags - 1=only the param is given to fkt (otherwise there is also given the element and the event), 2=event is not given to the element var f; if (behavior & 1) f = function (ev) { if (!ev) ev = window.event; fkt.call(_this,param); if (behavior & 2) { if (ev.preventDefault) ev.preventDefault(); return false; } } else f = function (ev) { if (!ev) ev = window.event; fkt.call(_this,this,ev,param); if (behavior & 2) { if (ev.preventDefault) ev.preventDefault(); return false; } } if (window.addEventListener) el.addEventListener(event, f, false); else el.attachEvent('on'+event,f); return false; }; var getAbsOffsetTop = function (el) { // returns the offset top of the given element seen from the document top (el CAN be in the current IFRAME) var pos = el.offsetTop; while (el.offsetParent) { if (el.offsetParent == el) break; el = el.offsetParent; pos += el.offsetTop; } try { if (el == frames[current].contentWindow.document.body || el == frames[current].contentWindow.document.documentElement) pos += getAbsOffsetTop(frames[current]); } catch (e) { } return pos; }; var getAnchorFromURL = /* private */ function (url) { var i = url.lastIndexOf('#'); if (i < 0) return ''; var a = url.substring(i+1, url.length); if ((/[^(A-Za-z0-9_)]/).test(a)) return ''; return a; }; var getSiteFromURL = /* private */ function (url) { var i = url.lastIndexOf('#'); var j = url.indexOf('?'); if (!differentQueryDifferentSites && j >= 0 && j < j) i = j; if (i < 0) i = url.length; return url.substring(0,i); }; var create = /* private */ function () { if ((!window.addEventListener && !window.attachEvent) || !document.body || !document.getElementsByTagName) { incompatibleBrowser = true; return; } if (!window.fapi) { incompatibleBrowser = true; throw 'DynamicIFrame benoetigt frameCommunicator.js!'; } if (window.jsw && jsw.units.switchableDesign) jsw.units.switchableDesign.unit.requireDesignFile('dynamicIFrame', box); selfHostname = getHostnameAndProtocol(self.location.href); // Re-Create Frame elements, because browser does stupid things on hard coded iframes when re-loading a site... startPage = frames[0].src; aTarget = frames[0].getAttribute('name', 0); aTarget2 = frames[1].getAttribute('name', 0); frames[1].parentElement.removeChild(frames[1]); frames[0].parentElement.removeChild(frames[0]); if (frames.length > 0) throw 'Not more than two frames allowed in HTML!'; var f = document.createElement('iframe'); f.setAttribute('name', aTarget); f.id = aTarget; FrameBox.appendChild(f); f = document.createElement('iframe'); f.setAttribute('name', aTarget2); f.id = aTarget2; FrameBox.appendChild(f); // prepare communicators communicators[0] = new FrameCommunicator(frames[0], (debugLogging)?'0':''); communicators[1] = new FrameCommunicator(frames[1], (debugLogging)?'1':''); communicators[0].autoHeight = true; communicators[1].autoHeight = true; communicators[0].onContentResize = function (w, h) { siteResize(0, h); }; communicators[1].onContentResize = function (w, h) { siteResize(1, h); }; communicators[0].onURLChanged = setQueryString; communicators[1].onURLChanged = setQueryString; communicators[0].onload = manipulateCurrentSite; communicators[1].onload = manipulateCurrentSite; communicators[0].onTitleChanged = titleChange; communicators[1].onTitleChanged = titleChange; if (document.addEventListener) document.addEventListener("DOMContentLoaded", domReady, false); else { var oldReadyStateChange = document.onreadystatechange; document.onreadystatechange = function () { try { oldReadyStateChange(); } catch (e) { } if(document.readyState == "interactive" || document.readyState == "complete") domReady(); } } // QueryString untersuchen if (window.addEventListener) window.addEventListener('popstate', evalQueryString, false); } var domReady = /* private */ function () { if (domWasReady) return; domWasReady = true; // Links auf JavaScript umstellen for (var i = 0; i < document.getElementsByTagName('a').length; i++) linkJSenhance(document.getElementsByTagName('a')[i]); if (nummerateMenu) buildMenuNumbers(); activateCurrentLink(); // AutoHeight-Elemente finden for (var i = 0; i < document.getElementsByTagName('div').length; i++) if (document.getElementsByTagName('div')[i].className.indexOf('NoJsHeight') >= 0) autoHeightEl.push(document.getElementsByTagName('div')[i]) if (!isAutoFrameHeight) { isAutoFrameHeight = true; autoFrameHeight(false); } // Scroll-Event setzen window.addEventListener('scroll', sitescroll, false); // Seite aus Querystring laden window.setTimeout(function () { // for whatever reason, events do not pass before dom content is loaded. if (scriptLoadingPage != '' || !evalQueryString()) { if (scriptLoadingPage != '') { debugLog('Load site set by script: '+scriptLoadingPage); loadSite(scriptLoadingPage, 1); } else { debugLog('Load start site: '+startPage); var hashtag = window.location.href.split('#'); if (hashtag.length > 1) hashtag = '#'+hashtag.pop(); else hashtag = ''; loadSite(startPage+hashtag, 1); } } }, 0); }; var debugLog = /* private */ function (msg) { if (!debugLogging) return; var d = new Date(); console.log('['+d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()+'] '+msg); } var evalQueryString = /* private */ function (effect) { // Wird in domready und beim klicken auf den Vorwaerts/Rueckwaerts-Button im Browser aufgerufen var newSite = self.location.href.split('?'); newSite.shift(); if (newSite.length == 0) return false; newSite[0] = newSite[0].split('&'); var isFB = false; for (var i = 0; i < newSite[0].length; i++) if (newSite[0][i].indexOf('fbclid=') == 0 || newSite[0][i].indexOf('\/gclid=') == 0) { newSite[0].splice(i, 1); isFB = true; break; } if (newSite[0].length == 0) return false; newSite[0] = newSite[0].join('&'); if (isFB) newSite[0] = newSite[0].split('%2F').join('\/'); var newSite = absolutePath(newSite.join('?')); if (isNotInWhitelist(newSite)) return false; debugLog('Load site by query: '+newSite); loadSite(newSite, (effect)?0:1); return true; }; this.addURLToWhitelist = /* public */ function (url) { var rep = new RegExp('([^\\w&^\\*])', 'g'); var host = getHostnameAndProtocol(url); var i = host.length; host = host.replace(rep, '\\$1'); var path = url.substring(i, url.length).replace(rep, '\\$1'); if (path == '' && host.length > 0 && host.charAt(host.length-1) == '*') path = '*'; while (host.length > 0 && host.charAt(host.length-1) == '*') host = host.substring(0, host.length-1); if (host == '') throw 'URL has wrong syntax'; rep = new RegExp('\\*', 'g'); path = path.replace(rep, '[a|^a]*'); // innerhalb des path-Bereiches koennen die * fuer beliebige Inhalte stehen // innerhalb des hostname-Bereiches haben die * spezielle Aufgaben var a = host.split('\\\:\\\/\\\/'); if (a.length == 1) { a = a[0].split('*'); if (a.length == 2) host = a[0] + '(\\w)*\\:\\/\\/((\\w)+\\.)*' + a[1]; else throw 'URL has wrong syntax'; } else if (a.length == 2) { rep = new RegExp('(\\*)', 'g'); a[0] = a[0].replace(rep, '(\\w)*'); a[1] = a[1].replace(rep, '((\\w)+\\.)*'); host = a[0] + '\\:\\/\\/' + a[1]; } else throw 'URL has wrong syntax'; whitelist.push(new RegExp('\^'+host+path)); }; var isNotInWhitelist = /* private */ function (src) { if (getHostnameAndProtocol(src) == selfHostname) return false; for (var i = 0; i < whitelist.length; i++) if (whitelist[i].test(src)) return false; return true; }; var buildMenuNumbers = /* private */ function () { if (!nummerateMenu) return; for (var i = 0; i < menuLinks.length; i++) { if (menuLinks[i]['el'].parentElement.tagName.toLowerCase() != 'li') continue; var ol = menuLinks[i]['el'].parentElement; var a = new Array(); while (ol.parentElement && ol.parentElement.tagName.toLowerCase() == 'ol') { var nr = 1; var el = ol.parentElement.firstElementChild; while (el && el != ol) { el = el.nextElementSibling; if (el.tagName.toLowerCase() == 'li') nr++; } a.unshift(nr); ol = ol.parentElement; } if (!menuLinks[i]['numberEl']) { menuLinks[i]['numberEl'] = document.createElement('span'); menuLinks[i]['el'].insertBefore(menuLinks[i]['numberEl'], menuLinks[i]['el'].firstChild); } menuLinks[i]['numberEl'].innerHTML = a.join('.')+'. '; } }; var activateLink = /* private */ function (link) { // das in link uebergebene MenuLink-Objekt wird aktiviert, ggf. akuell aktive Links werden deaktiviert if (window.opera && link) { link['el'].style.visibility = 'hidden'; link['el'].style.visibility = 'visible'; } // Komisches Bug if (link && link['active']) return; for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['active']) { menuLinks[i]['active'] = false; var c = menuLinks[i]['el'].className.split(' '); for (var j = c.length-1; j >= 0; j--) if (c[j] == 'active') c.splice(j, 1); menuLinks[i]['el'].className = c.join(' '); } if (!link) { if (onLinkActivate) onLinkActivate(null); return; } link['active'] = true; link['el'].className += ' active'; if (onLinkActivate) onLinkActivate(link['el']); activateCurrentHirachy(); }; var activateCurrentHirachy = /* private */ function () { var link = null; for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['active']) { link = menuLinks[i]; break; } if (!link || !currentHirachy) return; var found = false; for (var i = currentHirachy.length-1; i >= 0; i--) if (found) { if (currentHirachy[i].link && currentHirachy[i].link['el'].getAttribute('alwaysactive') == 'yes' && !currentHirachy[i].link['active']) { currentHirachy[i].link['active'] = true; currentHirachy[i].link['el'].className += ' active'; } } else if (currentHirachy[i].link == link) found = true; }; var activateCurrentLink = /* private */ function () { // Seite und Anker ermitteln var site = currentSite; var anchor = getAnchorFromURL(communicators[current].contentURL); if (site.indexOf('?') != -1) site = site.substring(0, site.indexOf('?')); // Links mit gleicher Seite suchen var a = new Array(); for (var i = 0; i < menuLinks.length; i++) if (site == menuLinks[i]['site']) a.push(menuLinks[i]); // Keinen passenden Link gefunden if (a.length == 0) { activateLink(null); return; } // Falls aktueller Anker direkt existiert for (var i = 0; i < a.length; i++) if (a[i]['anchor'] == anchor) { activateLink(a[i]); return; } // Falls keine Ueberschriften-Anker bekannt if (currentSiteAnchors.length == 0) { activateLink(a[0]); return; } // Falls verknuepfter menuLink schon bekannt if ((currentSiteAnchors.length > anchorPos) && currentSiteAnchors[anchorPos]['link']) { activateLink(currentSiteAnchors[anchorPos]['link']); return; } // Anker-Rangfolge erstellen var b = new Array(); var j = anchorPos-1; var h = currentSiteAnchors[anchorPos]['hNr']; b[currentSiteAnchors[anchorPos]['hNr']] = currentSiteAnchors[anchorPos]; for (var i = anchorPos-1; i >= 0; i--) if (currentSiteAnchors[i]['hNr'] < h) { h = currentSiteAnchors[i]['hNr']; b[h] = currentSiteAnchors[i]; } // naheliegensten Menulink aktivieren for (var i = b.length-1; i >= 0; i--) { if (!b[i]) continue; for (var j = 0; j < a.length; j++) if (a[j]['anchor'] == b[i]['name']) { activateLink(a[j]); return; } } activateLink(a[0]); }; var setQueryString = /* private */ function () { // Diese Funktion haengt an die URL der aktuellen Seite das ?UrlVonFrame an. var P = communicators[current].contentURL; if (P == '') P = communicators[current].contentHostname; if (P == '' || P.indexOf('about:blank') == 0) return; currentSite = getSiteFromURL(P); if (isNotInWhitelist(P)) { frames[current].style.visibility = 'hidden'; frames[current].setAttribute('notInWhitelist', 'yes'); } else { frames[current].style.visibility = 'visible'; frames[current].removeAttribute('notInWhitelist'); } activateCurrentLink(); var L = self.location.href; i = L.indexOf('?'); if (i < 0) i = L.length; var Q = L.substring(i+1, L.length); L = L.substring(0, i); i = L.indexOf('#'); if (i > 0) L = L.substring(0, i); if (P.split('#')[0].toLowerCase() == startPage.toLowerCase()) { if (P.split('#').length == 2) P = '#'+P.split('#').pop(); else P = ''; } else { var H = getHostnameAndProtocol(communicators[current].contentURL); if (H == '') return; if (selfHostname == H) P = P.substring(H.length, P.length); if (P == Q) return; P = '?' + P; } if (history.pushState && window.location.href != L+P) { var stateObj = { foo: "bar" }; var title = ''; try { title = frames[current].contentWindow.document.title; } catch (e) {} if (oldQuery.split('#')[0] == P.split('#')[0]) history.replaceState(stateObj, document.title+': '+title, L+P); else history.pushState(stateObj, document.title+': '+title, L+P); oldQuery = P; } }; this.linkJSenhance = function (el, inFrameSite=false) { // replace simple links by javascript-enhanced links if (inFrameSite) { if ((el.target == '_self' || el.target == '') && el.href != '') { var a = el.href.split('#'); if (a[0].indexOf('javascript:') != 0 && a[0].indexOf('callto:') != 0 && a[0].indexOf('mailto:') != 0 && a[0].indexOf('webcal:') != 0 && el.download == '') { // check, this is a common link if (isNotInWhitelist(el.href)) el.target = '_blank'; else if (a[0] == frames[current].contentWindow.location.href.split('#')[0] && a.length > 1) onEvent(el, 'click', scrollTo, a[1], 3); else if ((' '+el.className+' ').indexOf(' WindowLink ') == -1) onEvent(el, 'click', loadSite, el.href, 3); } } } else { if (el.target == aTarget) { if ((' '+el.className+' ').indexOf(' MenuLink ') >= 0) { // Menue-Links, die hervorgehoben werden sollen, vorbereiten var href = absolutePath(el.href); menuLinks.push({ // menuLinks koennen auch in manipulateCurrentSite erzeugt werden! el : el, href : href, anchor : getAnchorFromURL(href), site : getSiteFromURL(href), subMenuId : el.getAttribute('submenu'), active : false }); } onEvent(el, 'click', loadSite, el.href, 3); } if (el.href.toLowerCase() == 'javascript:'+aTarget.toLowerCase()+'.print()') printLinks.push(el); } }; var scanElements = function (el, comm) { for (var i = 0; i < el.childNodes.length; i++) if (el.childNodes[i].nodeType == 1) { var eli = el.childNodes[i]; var tagName = eli.tagName.toLowerCase(); if (tagName == 'a') linkJSenhance(eli, true); else if ((/h\d/i).test(tagName)) { // add headlines to currentSiteAnchors // find or create headline's id var id = eli.id; if (!id && eli.getElementsByTagName('a').length > 0) id = eli.getElementsByTagName('a')[0].id; if (!id) { // auto-generate an id var txt = eli.textContent.toLowerCase(); id = ''; for (var j = 0; j < txt.length; j++) if (txt.charAt(j) == ' ' && id != '') id += '_'; else if (txt.charCodeAt(j) >= 97 && txt.charCodeAt(j) <= 122) id += txt.charAt(j); else if (txt.charCodeAt(j) >= 48 && txt.charCodeAt(j) <= 57 && id != '') id += txt.charAt(j); else if (txt.charCodeAt(j) == 228) id += 'ae'; else if (txt.charCodeAt(j) == 246) id += 'oe'; else if (txt.charCodeAt(j) == 252) id += 'ue'; else if (txt.charCodeAt(j) == 223) id += 'ss'; var j = 2; while (document.getElementById(id)) { id += '_'+j; j++; } eli.id = id; } // add headline to current site anchors currentSiteAnchors.push({ el: eli, hNr: parseInt(tagName.substring(1)), id: id, content: eli.textContent, site: comm.contentURL.split('#')[0], pos: getAbsOffsetTop(eli), link: null }); } scanElements(eli, comm); } } var manipulateCurrentSite = /* private */ function (comm) { // wird von iframe.communicator.onload aufgerufen, sobald der Inhalt des frames geladen hat und dessen Hoehe bekannt ist. var _this = this; var frameNr = communicators.indexOf(comm); if (frameNr != current) return; debugLog('manipulate frame '+frameNr+'; url='+comm.contentURL); currentSiteAnchors = new Array(); anchorPos = 0; try { // Innerhalb von try-catch: Aenderungen an der Frame-Seite frames[current].contentWindow.document.body.style.overflow = (isAutoFrameHeight)?'auto':'hidden'; if (window.jsw) window.jsw.processSite(frames[current].contentWindow.document.body, jswMainWindow); scanElements(frames[current].contentWindow.document.body, comm); } catch (e) {} // find this site's root menu link (first menu link, that points to this site) var hirachy = new Array(); hirachy.push(new Object()); hirachy[0]['link'] = null; hirachy[0]['subMenu'] = null; for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['site'] == comm.contentURL.split('#')[0]) { hirachy[0]['link'] = menuLinks[i]; hirachy[0]['subMenu'] = null; if (menuLinks[i]['el'].getAttribute('submenu')) hirachy[0]['subMenu'] = document.getElementById(menuLinks[i]['el'].getAttribute('submenu')); if (!hirachy[0]['subMenu'] && menuLinks[i]['el'].parentElement.tagName.toLowerCase() == 'li') { var nextEl = menuLinks[i]['el'].parentElement.nextElementSibling; while (nextEl) if (nextEl.tagName.toLowerCase() == 'li' || nextEl.tagName.toLowerCase() == 'ol') break; else nextEl = nextEl.nextElementSibling; if (nextEl && nextEl.tagName.toLowerCase() == 'ol') hirachy[0]['subMenu'] = nextEl; else if (autoFillMenu > 0) { hirachy[0]['subMenu'] = document.createElement('ol'); if (nextEl) menuLinks[i]['el'].parentElement.parentElement.insertBefore(hirachy[0]['subMenu'], nextEl); else menuLinks[i]['el'].parentElement.parentElement.appendChild(hirachy[0]['subMenu']); } } break; } // here, hirachy[0] contains the root link to this site and the destination ol-list for headline anchors. Now expand hirachy towards higher levels linkBaseSearch: while (hirachy[0]['link'] && hirachy[0]['link']['el'].parentElement.tagName.toLowerCase() == 'li') { var ol = hirachy[0]['link']['el'].parentElement.parentElement; while (ol.tagName.toLowerCase() == 'ol') { hirachy.unshift({ link: null, subMenu: ol }); if (ol.id != '') for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i].subMenuId == ol.id) { hirachy[0].link = menuLinks[i]; continue linkBaseSearch; } ol = ol.parentElement; } break; } // actualize menu with currentSiteAnchors var h = hirachy.length-1; if (h >= 0 && hirachy[h]['subMenu'] && currentSiteAnchors.length > 0) { for (var i = 0; i < currentSiteAnchors.length; i++) { // search or create related links for all anchors (don't do this in try..catch) // find related menu link to this anchor var isSingleTopHeadline = (currentSiteAnchors[i]['el'].tagName.toLowerCase() == 'h1') && (currentSiteAnchors[i]['el'].ownerDocument.getElementsByTagName('h1').length == 1); for (var j = 0; j < menuLinks.length; j++) if (menuLinks[j]['site'] == comm.contentURL.split('#')[0] && (menuLinks[j]['anchor'] == currentSiteAnchors[i]['id'] || isSingleTopHeadline && menuLinks[j]['anchor'] == '')) { var pel = menuLinks[j]['el'].parentElement; if (pel.tagName.toLowerCase() != 'li') if (isSingleTopHeadline) { currentSiteAnchors[i]['link'] = menuLinks[j]; currentSiteAnchors[i]['isSingleTopHeadline'] = true; break; } else continue; // this link is not a list-element. Ignore! while (pel) if (pel == hirachy[0]['subMenu']) break else pel = pel.parentElement; if (!pel) continue; // this link is outside of the hirachy-ol and has to remain untouched! currentSiteAnchors[i]['link'] = menuLinks[j]; break; } var link = currentSiteAnchors[i]['link']; if (link) { // remove li-element from DOM (maybe add it to an other location, later) if (link == hirachy[h]['link']) continue; // don't touch root link link['el'].parentElement.parentElement.removeChild(currentSiteAnchors[i]['link']['el'].parentElement); } else if (currentSiteAnchors[i]['hNr'] <= autoFillMenu && (!currentSiteAnchors[i]['el'] || currentSiteAnchors[i]['el'].getAttribute('autocompletemenu') !== 'no')) { // create a new menu link, if missing link = { href: comm.contentURL.split('#')[0] + '#' + currentSiteAnchors[i]['id'], anchor: currentSiteAnchors[i]['id'], site: comm.contentURL.split('#')[0], active: false, el: document.createElement('a') }; link['el'].textContent = currentSiteAnchors[i]['content']; link['el'].className = 'MenuLink'; link['el'].target = aTarget; link['el'].href = link['href']; onEvent(link['el'], 'click', loadSite, link['href'], 3); currentSiteAnchors[i]['link'] = link; document.createElement('li').appendChild(link['el']); menuLinks.push(link); } if (!link) continue; // insert menu link at correct position, create missing ol-hirachies if (i == 0 || currentSiteAnchors[i-1]['isSingleTopHeadline']) hirachy[h]['subMenu'].insertBefore(link['el'].parentElement, hirachy[h]['subMenu'].firstChild); else { var hnr = currentSiteAnchors[i-1]['hNr']; var nextSiblingForLi = currentSiteAnchors[i-1]['el'].parentElement.nextSibling; // enter necessary sub-menus while (hnr < currentSiteAnchors[i]['hNr']) { var newOl = null; if (hnr == currentSiteAnchors[i-1]['hNr']) { if (currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling && currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling.tagName.toLowerCase() == 'ol') newOl = currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling; else { newOl = document.createElement('ol'); hirachy[h]['subMenu'].insertBefore(newOl, currentSiteAnchors[i-1]['el'].parentElement.nextSibling); } } else { if (hirachy[h]['subMenu'].firstElementChild && hirachy[h]['subMenu'].firstElementChild.tagName.toLowerCase() == 'ol') newOl = hirachy[h]['subMenu'].firstElementChild; else { newOl = document.createElement('ol'); hirachy[h]['subMenu'].insertBefore(newOl, hirachy[h]['subMenu'].firstChild); } } hnr++; h++; hirachy.push({ link: null, subMenu: newOl }); nextSiblingForLi = newOl.firstChild; } // exit unneccessary sub-menus while (hnr > currentSiteAnchors[i]['hNr'] && h > 0) { nextSiblingForLi = hirachy[h]['subMenu'].nextSibling; h--; hnr--; hirachy.pop(); } hirachy[h]['subMenu'].insertBefore(link['el'].parentElement, nextSiblingForLi); } } } currentHirachy = hirachy; activateCurrentHirachy(); // Nummerierung if (nummerateMenu) { buildMenuNumbers(); for (var i = 0; i < currentSiteAnchors.length; i++) if (currentSiteAnchors[i]['link'] && currentSiteAnchors[i]['link']['numberEl']) { var nrEl = currentSiteAnchors[i]['el'].getElementsByTagName('span')[0]; if (!nrEl || (' '+nrEl.className+' ').indexOf(' number ') < 0) { nrEl = frames[current].contentWindow.document.createElement('span'); currentSiteAnchors[i]['el'].insertBefore(nrEl, currentSiteAnchors[i]['el'].firstChild); } nrEl.innerHTML = currentSiteAnchors[i]['link']['numberEl'].innerHTML; } } if (onMenuChange) onMenuChange(); // sonstiges setQueryString(); blendInfo['currentSiteReady'] = true; if (onSiteLoaded) { debugLog('Call onSiteLoaded'+((loadWithoutEffect)?' without effect':'')); onSiteLoaded(frames[current].contentWindow, loadWithoutEffect); } loadingSiteAnchor = getAnchorFromURL(communicators[current].contentURL); if (loadWithoutEffect && loadingSiteAnchor != '') { debugLog('Try to scroll to #'+loadingSiteAnchor); scrollTo(loadingSiteAnchor, 2); } else if (loadingSiteAnchor == '') scrollTo(0, 2); }; var sitescroll = /* private */ function () { // funktion fuer Scrollevent if (!autoChangeAnchor || currentSiteAnchors.length == 0 || scrollInfo['scrolling']) return; // richtigen Anker ermitteln var pos = window.pageYOffset; var nr = anchorPos; while (nr > 0 && currentSiteAnchors[nr]['pos'] > pos + 100) nr--; while (nr < currentSiteAnchors.length-1 && currentSiteAnchors[nr+1]['pos'] <= pos + 100) nr++; // Ankerposition testen if (!currentSiteAnchors[nr]) return; // ToDo: unknown error! var top = getAbsOffsetTop(currentSiteAnchors[nr]['el']); if (Math.abs(top - currentSiteAnchors[nr]['pos']) > 5) { currentSiteAnchors[nr]['pos'] = top; sitescroll(); return; } if (pos < getAbsOffsetTop(frames[current])) nr = -1; // Anker wechseln if (nr != anchorPos) { anchorPos = nr; var i = frames[current].contentWindow.location.href.lastIndexOf('#'); if (i < 0) i = frames[current].contentWindow.location.href.length; var stateObj = { foo: "bar" }; var anchorHashtag = ''; if (nr >= 0) anchorHashtag = '#'+currentSiteAnchors[nr]['id']; frames[current].contentWindow.history.replaceState(stateObj, '', frames[current].contentWindow.location.href.substring(0,i)+anchorHashtag); communicators[current].newURL(frames[current].contentWindow.location.href.substring(0,i)+anchorHashtag); // Bugfix: Beim reload der Seite werden im Frame die Funktionen replaceState und pushState nicht ueberwacht } }; var autoFrameHeight = /* private */ function (auto) { // wenn true uebergeben wird, wird die Hoehe des iframe-Bereiches auf ~100% der Seitenhoehe (das ClassName 'NoJsHeight' wird wieder an alle Elemente, die dies zu Anfang trugen, angehaengt) angepasst, ansonsten richtet sich die Hoehe nach dem Inhalt des aktuellen frames. if (auto == isAutoFrameHeight) { debugLog('Auto frame height already '+((auto)?'activated':'deactivated')+'.'); return; } debugLog(((auto)?'Activate':'Deactivate')+' auto frame height...'); isAutoFrameHeight = auto; for (var i = 0; i < autoHeightEl.length; i++) { var a = autoHeightEl[i].className.split(' '); var n = ''; for (var j = 0; j < a.length; j++) { if (a[j] == 'NoJsHeight') continue; if (n != '') n += ' '; n += a[j]; } autoHeightEl[i].className = n; } if (auto) { for (var i = 0; i < autoHeightEl.length; i++) { if (autoHeightEl[i].className != '') autoHeightEl[i].className = autoHeightEl[i].className + ' '; autoHeightEl[i].className = autoHeightEl[i].className + 'NoJsHeight'; } frames[current].style.height = '100%'; try { frames[current].contentWindow.document.body.style.overflow = 'auto'; } catch (e) { } } else { try { frames[current].contentWindow.document.body.style.overflow = 'hidden'; } catch (e) { } } }; var siteResize = /* private */ function (nr, h) { if (nr != current) return; debugLog('Resize info from frame '+nr+' received. Height = '+h); if (loadingSiteAnchor != '') scrollTo(loadingSiteAnchor, 2); if (onContentResize) onContentResize(communicators[current].contentWidth, communicators[current].contentHeight, communicators[current]); if (blendInfo['blending'] && !(blendInfo['state'] & 2)) return; // kein Resize fuer noch nicht sichtbare Seiten if (blendInfo['blending'] && isAutoFrameHeight && h >= 0) frames[next].style.height = frames[next].offsetHeight + 'px'; // automatisch gegebene Hoehe einfrieren, bevor autoFrameHeight false wird autoFrameHeight(h < 0); }; var titleChange = /* private */ function (title, communicator) { if (communicator != communicators[current]) return; if (onTitleChange) onTitleChange(title); }; var blend = /* private */ function () { // Loop fuer Einblendeeffekt if (!blendInfo['timer']) blendInfo['timer'] = window.setInterval(blend, 40); var t = getTickCount(); // blendState - Flags: 1 = alter Frame wurde ausgeblendet 2 = neue Seite hat geladen und Verzoegerung ist um 4 = neue Seite wurde eingeblendet if ((blendInfo['state'] & 1) != 1 && !blendInfo['browse']) { // Wenn Flag 1 nicht gesetzt (alte Seite ausblenden) if (t - blendInfo['start'] < blendOutTime) { if (blendOutEffect) blendOutEffect(frames[next], (t - blendInfo['start'])/blendOutTime, false); } else { blendInfo['state'] += 1; frames[next].style.display = 'none'; communicators[next].setSrc('about:blank'); if (blendOutEffect) blendOutEffect(frames[next], -1, false); } } if (((blendInfo['state'] & 2) != 2) && (blendInfo['currentSiteReady'] || t - blendInfo['start'] > maxWaitForSite) && (t - blendInfo['start'] > blendInDelay)) { // Wenn Flag 2 nicht gesetzt und Bedingungen fuer Einblenden von neuer Seite erfuellt (neue Seite anzeigen) if (blendInEffect && !blendInfo['browse']) blendInEffect(frames[current], 0, true); else if (browseEffect && blendInfo['browse']) browseEffect(frames[current], frames[next], 0, blendInfo['dir']); frames[current].style.maxHeight = 'none'; blendInfo['startDelay'] = t; blendInfo['state'] += 2; siteResize(current, communicators[current].contentHeight); if (blendInfo['browse']) { if (blendInfo['dir']) loadingSiteAnchor = communicators[current].contentHeight + frames[current].parentElement.offsetTop - window.innerHeight; else loadingSiteAnchor = 0; } if (blendInfo['browse']) scrollTo(loadingSiteAnchor, 5); else if (loadingSiteAnchor != '') scrollTo(loadingSiteAnchor, 1); } if (((blendInfo['state'] & 4) != 4) && (blendInfo['state'] & 2)) { // wenn Flag 2 gesetzt aber Flag 4 noch nicht (neue Seite einblenden) if (blendInfo['browse']) { if (t - blendInfo['startDelay'] < browseDuration) { if (browseEffect) browseEffect(frames[current], frames[next], (t-blendInfo['startDelay'])/browseDuration, blendInfo['dir']); } else { blendInfo['state'] |= 5; frames[next].style.display = 'none'; communicators[next].setSrc('about:blank'); if (browseEffect) browseEffect(frames[current], frames[next], -1, blendInfo['dir']); } } else { if (t - blendInfo['startDelay'] < blendInTime) { if (blendInEffect) blendInEffect(frames[current], (t - blendInfo['startDelay'])/blendInTime, true); } else { blendInfo['state'] |= 4; if (blendInEffect) blendInEffect(frames[current], -1, true); } } } if ((blendInfo['state'] & 5) == 5) { // wenn Flag 1 und 4 gesetzt (Blendvorgang beenden) if (blendInfo['timer']) window.clearInterval(blendInfo['timer']); blendInfo['timer'] = null; blendInfo['blending'] = false; } }; this.loadSite = /* public */ function (src, behavior) { // behavior : 0=normal, 1=kein Effekt, 2=blaettern, 4=nach oben blaettern if (!behavior) behavior = 0; if (incompatibleBrowser || src == 'about:blank') { debugLog('Incompatible site load blocked: '+src); return; } src = absolutePath(src); if (isNotInWhitelist(src)) { debugLog('Not whitelisted site blocked: '+src); return; } if (!domWasReady && behavior == 1) { this.scriptLoadingPage = src; return; } // Wenn neue Seite gleich aktueller Seite, nur scrollTo ausfuehren if (getSiteFromURL(src) == currentSite) { if (~behavior & 2) scrollTo(getAnchorFromURL(src)); debugLog('Loading of site blocked, because it already is loaded! ('+src+')'); return; } // Einstellungen loadWithoutEffect = (behavior & 1) == 1; if (loadWithoutEffect) debugLog('Load without effect'); else debugLog('Load with effect'); currentSiteAnchors = new Array(); anchorPos = 0; // Wenn Effekt aktiv, nur neue Seite in aktuellen Frame laden if (blendInfo['blending']) { debugLog('Replacing url of currently loading site in frame '+current+' with '+src); communicators[current].setSrc(src); return; } // current und next wechseln current = next; next = (current == 0)?1:0; // neue Seite laden if (!loadWithoutEffect) { blendInfo['currentSiteReady'] = false; blendInfo['blending'] = true; } debugLog('Frame '+current+' starts loading of '+src+' | behavior = '+behavior); communicators[current].setSrc(src); // PrintLinks umstellen if (current == 0) for (var i = 0; i < printLinks.length; i++) printLinks[i].href = 'javascript:'+aTarget+'.print()'; else for (var i = 0; i < printLinks.length; i++) printLinks[i].href = 'javascript:'+aTarget2+'.print()'; // Blenduebergang starten if (loadWithoutEffect) { frames[next].style.display = 'none'; frames[current].style.display = 'block'; } else { frames[current].style.maxHeight = '0px'; frames[current].style.display = 'block'; // display = block ist notwendig, damit Seite im Frame beginnt zu laden! blendInfo['state'] = 0; blendInfo['browse'] = (behavior & 2) == 2; blendInfo['dir'] = (behavior & 4) == 4; blendInfo['start'] = getTickCount(); blend(); } }; var scroll = /* private */ function () { // Loop fuer Scroll-Effekt if (!scrollInfo['timer']) scrollInfo['timer'] = window.setInterval(scroll, 40); var t = getTickCount(); var d = scrollInfo['blendEffectTime']; if (scrollInfo['start'] + d > t) { var s = (t - scrollInfo['start']) / d; s = scrollInfo['fkt'](s); s = scrollInfo['startPos'] * (1-s) + scrollInfo['endPos'] * s; window.scrollTo(window.pageXOffset, s); } else { scrollInfo['scrolling'] = false; if (scrollInfo['timer']) window.clearInterval(scrollInfo['timer'], 40); scrollInfo['timer'] = null; window.scrollTo(window.pageXOffset, scrollInfo['endPos']); } }; this.scrollTo = /* public */ function (target, scrollbehavior) { // scrollbehavior: 0=normal, 1=scrollt so lange, wie Einblendeeffekt, 2=springt sofort zu neuer Position (kein Effekt), 4=scrollFkt = browseScrollFkt if (!scrollbehavior) scrollbehavior = 0; if (incompatibleBrowser) return; if (scrollbehavior & 4) scrollInfo['fkt'] = browseScrollFunction; else scrollInfo['fkt'] = scrollFunction; // Scrollziel suchen var pos = 0; if (!isNaN(target)) pos = target; else { try { var el = frames[current].contentWindow.document.getElementById(target); if (!el) el = frames[current].contentWindow.document.getElementsByName(target)[0]; if (el) pos = getAbsOffsetTop(el); } catch (e) { return; } try { var L = frames[current].contentWindow.location.href; var i = L.lastIndexOf('#'); if (i > 0 && L.lastIndexOf('?') < i) L = L.substring(0, i); var stateObj = { foo: "bar" }; if (frames[current].contentWindow.location.href != L+'#'+target) frames[current].contentWindow.history.replaceState(stateObj, document.title+': '+frames[current].contentWindow.document.title, L+'#'+target); } catch (e) { } } // Scrollziel setzen if (loadingSiteAnchor != '' && getAbsOffsetTop(frames[current]) + frames[current].offsetHeight >= pos) { debugLog('Anchor of loading site is reachable.'); loadingSiteAnchor = ''; } scrollInfo['endPos'] = pos; if (scrollInfo['scrolling']) return; // Scrolleffekt starten scrollInfo['scrolling'] = true; scrollInfo['start'] = getTickCount(); if (scrollbehavior & 2) scrollInfo['start'] = 0; scrollInfo['startPos'] = window.pageYOffset; scrollInfo['blendEffectTime'] = scrollTime; if (scrollbehavior & 1) scrollInfo['blendEffectTime'] = blendInTime; if (scrollbehavior & 5 == 5) scrollInfo['blendEffectTime'] = browseDuration; scroll(); return; }; this.enableOverscroll = function () { if (overscrollListener) return; overscrollListener = createOverscrollListener(document, 1000, function (dir) { for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['active']) { if (dir && i < menuLinks.length - 1) { loadSite(menuLinks[i+1].href, 2); activateLink(menuLinks[i+1]); } else if (!dir && i > 0) { loadSite(menuLinks[i-1].href, 6); activateLink(menuLinks[i-1]); } return; } }); }; this.getActiveContentWindow = function () { return frames[current].contentWindow; }; this.getActiveCommunicator = function () { return communicators[current]; }; create(); // Objekt sollte vollstaendig geladen sein, bevor Create aufgerufen wird } }