jQuery(document).ready(function () {
  try { // errorhandler
    // global namespace aliases
    var $ = jQuery;
    var lib = hsp;

    // save some values for convenience and speed
    var $cvdiv = $('#centerview-div');
    var $cvcont = $('#centerview-content');
    var $content = $('#content');
    var $ajaxcontent = $('#ajaxcontent');
    var $navToggle = $('#nav-toggle');
    var $toggleOben = $('#toggle-oben');
    var $moveAll = $('#move-all');
    var $contentBackground = $('#content-background');
    var $boxenrahmen = $('#boxenrahmen');
    var ios = lib.ios;
    var stage = lib.stage;
    //var linkclicked = false; not needed, we use the existence of the callback instead
    var linkclickedCallback = undefined; // if this is undefined, it means !linkclicked
    var historyLoad,tabHover;

    // move the cvcont to the left cube for ios devices
    if (ios) {
      $contentBackground.append($cvcont);
    }

    // aliases for the other components
    var menu = $.fn.accmenu;
    var centerview = $.fn.centerview;
    var acco = $.fn.acco;


    // for avoiding reentrance:
    var busy = false;
    var busyTime;
    var nextLink = undefined; // must be set to undefined, as the empty string might be a link
    function setBusy(info) {
      lib.info('setBusy called by '+info+', busy = '+busy);
      if (busy) {
        lib.warn('setBusy called while busy is true');
      }
      busy = true;
      busyTime = new Date().getTime();
    }
    function isBusy(info) {
      lib.info('isBusy called by '+info+', busy = '+busy);
      if (busy) {
        var ct = new Date().getTime();
        var diff = ct - busyTime;
        lib.info ('Page busy since '+diff+'ms');
        if (diff > 10000) {
          lib.error('Releasing busy state after 10 seconds');
          busy = false;
        // TODO: check for running ajax request, or running animations
        }
      }
      return busy;
    }
    function notBusy(info) {
      lib.info('notBusy called by '+info+', busy = '+busy);
      if (!busy) {
        lib.warn('notBusy called while busy is false');
      }
      busy = false;
      busyTime = new Date().getTime();
    }

    // Constants:
    //var mode = 'small';
    var navWidth = 230;
    var navPadding = 20;
    var contentWidth = 970;
    // the minimum width of the window to switch to 'big' mode,
    // where nav and content are displayed side by side
    var minimumWidth = navWidth + navPadding + contentWidth;
  
    // all action which have to be done at the first document.ready go here
    // called from the end of the document ready fn
    function initPage() {
      
      disableIFrames($content);
      removeContextMenu();
      setUpTabs();
      // set up the vertical tabs
      // see the 'rebind' function, too
      $('.verticaltab').verticaltab();
      setUpCenterview();
      setUpAccmenu();
      $('#navibar-content').css({
        zIndex : 1000
      });
      setCurrentContent('')
      // the margin left is still set to 50%, must be set to 0 to support the new positioning method
      $moveAll.css('marginLeft', 0);
      setMode();
      $(window).resize(setMode);
      //setUpHistory();
      setUpFFHistory();
      // We 'hijack' all clicks on the page
      $(document).bind('click',handleClick);
      setUpSlidein();

      dealWithStartPath();
      $(document).keyup(function keyup(e){
        //console.log('keyup:'+e.keyCode);
        // ESC
        if (e.keyCode === 27)
        {
          centerviewClose(e);
        }
        // F2
        if (e.keyCode === 113){
          menu.toggle();
        }
      //if (e.keyCode === 78) {
      // $.fn.navileft.toggle();
      //}
      //if (e.keyCode === 79) {
      //toggleNavDir();
      //}
      });

      // this should be the last call:
      if (lib.divid) {
        // this means there has been saved a divid to be shown later
        // we show it and remove the variable
        setBusy('showdiv');
        var cb = function () {
          notBusy('showdiv');
        };
        centerview.showdiv(lib.divid,cb);
        lib.divid = undefined;
      }
        initKuenform();
    }

    function initKuenform() {
      lib.initKuenform();
    }

    // remove contextmenu on images, but only on production system
    function removeContextMenu() {
      if (stage == 'prod') {
        $('img').live(
          'contextmenu',
          function (ev) {
            var target = $(ev.target);
            if (target.hasClass('contextmenu')) {
              return true;
            } else {
              return false;
            }
          });
      }
    }

    function setUpTabs() {
      // set up tabs
      $('#left_tab').tabs();
      $('#right_tab').tabs();

      // make tabs visible
      $boxenrahmen.css('visibility','visible');
  

      // move the background images for selecting and unselecting tabs
      // this cannot be done in css, because the image has to be written in the <a> tag of the
      // tab in tabs.php
      // this setting will override everything we are doing in our css, so the only way is to do it
      // in javascript
      $('#boxenrahmen').bind('tabsselect', function(event, ui) {
        var id = $(ui.panel).attr('id');
        var link = $('#boxenrahmen a[href=#'+id+']');
        // we set this link to be selected
        link.css('background-position', '0 0px');
        // set the siblings to be deselected
        link.parent('li').siblings('li').find('a').css('background-position','0 40px');
      });
      lib.info('init tabHover');
      tabHover = function tabHover() {
        //hover effect for the tab navigation
        $('.ui-tabs-nav a').hover(function () {
          if ($(this).parent('li').hasClass('ui-tabs-selected')) {
            return;
          }
          $(this).css('background-position', '0 20px');
        }, function () {
          if ($(this).parent('li').hasClass('ui-tabs-selected')) {
            return;
          }
          $(this).css('background-position', '0 40px');
        });

      };
      lib.tabHover = tabHover;
      tabHover();
    }


    var centerviewClose = function centerviewClose(e) {

      if (isBusy('centerviewClose')) {
        e.preventDefault();
        return false;
      }
      setBusy('centerviewClose');
      linkclickedCallback = function () {
        notBusy('centerviewClose');
      }

      // attached to the click on the centerview close button
      var current = getCurrentContent();
      var withoutExt = removeExt(current);
      // check for leading /
      if (withoutExt.slice(0,1) != '/') {
        withoutExt = '/'+withoutExt;
      }
      if (withoutExt !== '/') {
        withoutExt = withoutExt + '.html';
      }
      if (current === 'must-reload') {
        withoutExt = '/';
      }
      lib.info('centerviewClose closing url: '+withoutExt+' currentContent: '+current);
      historyLoad(withoutExt);
      // block further link handling
      e.preventDefault();
      return false;
    }


    function setUpCenterview () {
      // with setdefault we set our defaults permanently
      // here we define, that always if the centerview hides, the tabs shall be
      // faded in
      if (ios) {

        centerview.setdefault({
          'width' : 960,
          'borderwidth' : 10,
          'borderheight' : 5,
          'onshow' : function () {
            acco.adjust();
          },
          'aftershow' : function () {
            recreateIFrames($cvcont);
          },
          'beforehide' : function () {
            disableIFrames($cvcont);
          },
          'onhide' : function () {
          },
          'duration' : 100,
          'close' : centerviewClose
        });
      } else {

        centerview.setdefault({
          'width' : 960,
          'borderwidth' : 10,
          'borderheight' : 5,
          'onshow' : function () {
            $('#boxenlinks').fadeOut();
            $('#boxenrechts').fadeOut();
            acco.adjust();
          },
          'aftershow' : function () {
          },
          'beforehide' : function () {
          },
          'onhide' : function () {
            $('#boxenlinks').fadeIn();
            $('#boxenrechts').fadeIn();
          },
          'duration' : 100,
          'close' : centerviewClose
        });
      }
      centerview.init();
    }


    function setUpAccmenu() {
      // set up the accmenu
      menu.setdefault({
        vonoben : lib.navioben,
        toggle : $navToggle,
        toggleOben : $toggleOben,
        toggleHeight : lib.navioben?0:undefined
      });
      $toggleOben.click(menu.toggle);
      $navToggle.click(menu.toggle);
      $('#navroot').accmenu();
    }



    function dealWithStartPath() {
      // deal with the start path and hash
      // to be called from end of document.ready
      var path;
      var hash=window.location.hash;
      if (hash) {
        path=hash.slice(1);
        // when using the history plugin, the plugin handles the current hash on initialization
        // but if we use html5 history we have to deal with a hash here
        // dealing with a hash is necessary if for example a  (hashed)link is sent from a user with an old browser
        // the modern browser has to be able to display this kind of
        if (lib.history) {
          // load the appropriate content
          loadFromHash(path);
          var state = {
            param : path
          };
          // we replace the old hashed url with the correct one
          window.history.replaceState(state, document.title, path);
        }
      } else {
        path = window.location.pathname;
      }

      // path contains now either the 'real' path, or, if there is an hash-attached path, this one
      if (path !== '/') {
        // we don't want the link to the start page marked
        menu.set(path);
        // as we start from another page than the startpage, we have to set this
        // as the current data, otherwise a link to the start page will not reload
        // content

        setCurrentContent('must-reload');
        // handle the link extension
        var ext = getlinkext(path);
        var cb;
        if (ext && lib.history) {
          lib.info('handle link ext in start path:'+ext);
          setBusy('dealStartPath');
          cb = function () {
            notBusy('dealStartPath');
          }
          handleLinkExt(ext,false,cb);
        } 
      }
    }

    function overlap(width) {
      // compute the free space on the left
      var s = (width-contentWidth) /2;
      // compare with nav width
      var d = navWidth-s;
      return Math.max(0,d);
    }

    // to be called once on load and on window.resize
    var setMode = function setMode() {
      var left;
      var w = $(window).width();
      
      if (ios) {
        // in ios we stay in mode 'small', no matter the window size
        left = Math.max(0, (w-contentWidth)/2);
        $moveAll.css('left',left);
        return
      }
      // checks window width and sets the mode accordingly
      // positions the moveAll div
      //console.log('overlap:'+o);
      if (w > minimumWidth) {
        // navi and content go side by side
        // now see if the content has to move
        left = Math.max(navWidth + navPadding, (w-contentWidth)/2);
        $moveAll.css('left',left);
        if (menu.mode == 'small') {
          menu.mode = 'big';
          // do things when going from small to big
          if (!menu.visible()) {
            menu.show();
          }
        }
      } else {
        // navi has to overlap
        // set the moveAll to the middle
        left = Math.max(0, (w-contentWidth)/2);
        $moveAll.css('left',left);
        if (menu.mode == 'big') {
          menu.mode = 'small';
        // do things when going from big to small
        }
      }
    }


    // ##################### Show the Centerview #########################
    var showcv = function showcv(cb) {
      menu.hideIfSmall();
      centerview.show(cb);
    };

    var hidecv = function hidecv(cb) {
      centerview.hide(cb);
    };

    var removeComment = function(html) {
      var res = html.replace(/<!--\s*remove-begin\s*-->(?:[\s\S]*?)?<!--\s*remove-end\s*-->/g,'');
      return res;
    }

    var reloadTabs = function() {
      $.ajax({
        dataType: 'html',
        url: '/ajax/tabs',
        success : function (html) {
          $boxenrahmen.html(html);
          // set up tabs
          $('#left_tab').tabs();
          $('#right_tab').tabs();
          lib.tabHover(); // funny enough, ie does not seem to know this object if it is not
        // attached to lib
        },
        error : function () {
          lib.warn('error reloading tabs');
        }
      });
    };
    // for testing the reloadTabs function
    //$('#contentrechts').click(reloadTabs);

    function getContentSaveLink(sds) {
      // gets a local string like /kontakt/impressum.karte.html and returns what shall be saved
      // in $content
      var res,i;
      res = '';
      if (sds === '') return res;
      res = removeExt(sds);
      // remove trailing slash
      i = res.indexOf('/');
      if (i === 0) {
        res = res.slice(1);
      }
      // remove trainling .html
      if (res.slice(-5) === '.html') {
        res = res.slice(0,-5);
      }
      return res;
    }
    
    function setCurrentContent(cleanLink) {
      $content.attr('data-param',cleanLink);
    }

    function getCurrentContent() {
      return $content.attr('data-param');
    }

    function isContentLoaded(param) {
      // checks if the given content is already loaded
      // compares only the real content path:
      // leading / is stripped off
      // url extension a la /profil.team.html is stripped off
      // trailing .html is stripped off
      var current,next;
      current = getCurrentContent();
      next = getContentSaveLink(param);
      //lib.info('old: '+current+' new: '+next);
      if (current === next) {
        return true;
      }
      return undefined;
    }

    function clearExtHandled() {
      $content.attr('data-ext-handled','');
    }

    function setExtHandled(ext) {
      var param = getCurrentContent();
      $content.attr('data-ext-handled',param+ext);
    }

    function wasCvOpenedViaExt() {
      var handled = $content.attr('data-ext-handled');
      return handled !== '';
    }

    function isExtHandled(ext) {
      var param = getCurrentContent();
      var handled = $content.attr('data-ext-handled');
      if (param+ext === handled) {
        return true;
      } else {
        return false;
      }
    }

    var aftercopy = function aftercopy() {
      // to be called after content has been copied which is about to be displayed
      //console.log('aftercopy called');
      $cvcont.find('.verticaltab > ul > li').click($.fn.verticaltab.clickhandler);
      $cvcont.find('.acco .acco-header').click($.fn.acco.clickhandler);
      $cvcont.find('.tooltip').tipsy();
    }

    var disableIFrames = function disableIFrames($elem) {
      if (!ios) return; // only for ios
      $('iframe',$elem).each(function () {
        var iframe = $(this);
        var src = iframe.attr('src');
        var iw = iframe.attr('width');
        var ih = iframe.attr('height');
        if(iw.indexOf('px') === -1) {
          // if the attribute does not contain px, we have to convert to a number
          iw = parseFloat(iw,10);
        }
        if(ih.indexOf('px') === -1) {
          // if the attribute does not contain px, we have to convert to a number
          ih = parseFloat(iw,10);
        }
        lib.info('remove iframe src: '+src);
        lib.info('iw '+iw+' ih '+ih);
        // Set src to some clickme info
        var parent = iframe.parent();
        iframe.remove();
        var rep = $('<a><div class="iframe-replacement"></div></a>');
        rep.find('div').css({
          'width' : iw,
          'height': ih
        });
        rep.attr('href',src);
        rep.attr('data-frameborder',iframe.attr('frameborder'));
        rep.attr('data-marginheight',iframe.attr('marginheight'));
        rep.attr('data-marginwidth',iframe.attr('marginwidth'));
        rep.attr('data-scrolling',iframe.attr('scrolling'));
        parent.append(rep);
      });
    }

    var recreateIFrames = function recreateIFrames($elem) {
      if (!ios) return;
      $('.iframe-replacement').each(function () {
        var rep = $(this);
        var link = rep.parent('a');
        if (!link.length) {
          lib.error('expected link around iframe replacement');
        }
        var src = link.attr('href');
        if (src.indexOf('maps.google.de') !== -1) {
          // we have a google maps iframe
          // do not recreate this, as it breaks the page
          return;
        }
        var iw = rep.css('width');
        var ih = rep.css('height');
        var iframe = $('<iframe/>');
        iframe.attr('src',src);
        iframe.attr('width',iw);
        iframe.attr('height',ih);
        iframe.attr('frameborder',link.attr('data-frameborder'));
        iframe.attr('marginheight',link.attr('data-marginheight'));
        iframe.attr('marginwidth',link.attr('data-marginwidth'));
        iframe.attr('scrolling',link.attr('data-scrolling'));
        lib.info('recreating iframe with src: '+src);
        link.after(iframe);
        link.remove();
      });
    }
  
    // ###################  loads new content in the content area via ajax ################
    // then handle link extension
    // or if content is already loaded, handle the link extension
    var loadContent = function loadContent(param, triggeredByLink,cb) {
      // loads the content with the given param via ajax
      lib.info('loadContent: '+param);
      menu.hideIfSmall();
      if (isContentLoaded(param)) {
        lib.info('content already loaded: "'+param+'"');
        // this was the content which was loaded last time, so it is already
        // visible
        var ext = getlinkext(param);
        if (ext) {
          //        alert('load content handling link extension');
          handleLinkExt(ext,triggeredByLink,cb);
        } else {
          // es there is no link extension, we have to clear this field
          hidecv(function () {

            clearExtHandled();
            cb();
          });
        }
      }
      else
      {
        //hidecv(); // TODO really necessary? what if we need a cv again
        // now load the content via ajax
        $.ajax({
          dataType: 'html',
          url: '/ajax/'+param,
          success: function(html) {
            // remove stylesheet links
            html = removeComment(html);
            // Each content page should start with a special comment (see: docs/readme.txt)
            // We extract the metainfo in this comment here
            var re=/^\s*<!--([\s\S]*?)-->/;
            var a = re.exec(html);
            if (a) {
              var meta = a[1]
              // In javascript, the dot never matches newlines, so we get the rest of the line,
              // where the line started with withespace - tit - more letters or whitespace - :
              var titleA = /[\s\r\n]*tit(?:[\w\s]*)?:(.*)/.exec(meta);
              //            var descA = /[\s\r\n]*des(?:[\w\s]*)?:(.*)/.exec(meta);
              //            var keywA = /[\s\r\n]*key(?:[\w\s]*)?:(.*)/.exec(meta);
              if (titleA) {
                var title = titleA[1];
                document.title = title;
              }
              // At this time, there is no need to deal with the description
              // and meta-tags information here
              //            if (descA) {
              //              var desc = descA[1];
              //              console.log(desc);
              //            }
              //            if (keywA) {
              //              var keyw = keywA[1];
              //              console.log(keyw);
              //            }
              // remove the meta-info comment from the string
              html = html.replace(re,'');
            }
            else {
              lib.warn('missing metadata for content: '+param);
            }
            // write content to page
            lib.info('succesfully loaded content: '+param);
            $content.html(html);

            disableIFrames($content);

            // save which content is loaded
            setCurrentContent(getContentSaveLink(param));
            
            // set up vertical tabs on new content
            $('#content').find('.verticaltab').verticaltab();
            //console.log('adding verticaltab clickhandler after ajax call');
            $('#content').find('.verticaltab > ul > li').click($.fn.verticaltab.clickhandler);

            // check if the param contains a link extension
            var linkext = getlinkext(param);
            if (linkext) {
              handleLinkExt(linkext,triggeredByLink,cb);
            } else {
              hidecv(function () {
                clearExtHandled();
                cb();
              });
            // no ext
            }
            // reload tabs
            if (!ios) {
              reloadTabs();
            }

            // initialize the kuenform, if it is on the page
            initKuenform();
          // call tracker (have to url-encode?)
          // track(param ? param : 'StartPage');

          },
          error: function (request, status, error) {
            lib.warn('error during ajax request: '+param);
            loadContent('error',false,cb);
          }
        });
      }
    }

    function normalCvVisible() {
      return centerview.normalCvVisible();
    }
  
    var getlinkext = function getlinkext(href) {
      // checks if the link has an extension part, if true
      // returns the extension with an # before it
      var regex = /(?:[\w\d/-]+)\.([\w\d-]+)\.html$/;
      var a = regex.exec(href);
      if (a) {
        // ok, we have a link extension
        return '#'+a[1];

      } else {
        // regex did not match
        return undefined;
      }
    }

    var removeExt = function removeExt(href) {
      var ext = getlinkext(href);
      if (ext) {
        // remove leading #
        ext = ext.slice(1);
        var res = href.replace('.'+ext+'.html', '.html');
        return res;
      } else {
        return href;
      }
    }

    // ################### Callback function for the history handling ######################
    var loadFromHash = function loadFromHash(hash) {
      // try {
      var hashHandled;
      var triggeredByLink = false;
      if (linkclickedCallback) {
        // we assume busy is set already
        triggeredByLink = true;
        //linkclicked = false;
        hashHandled = function () {
          // as the hash was triggered by a link click, we assume there is a linkclickedCallback,
          // execute it and remove it
          linkclickedCallback();
          linkclickedCallback = undefined;
          if (nextLink) {
            var h = nextLink;
            nextLink = '';
            loadFromHash(h);
          }
        }
      } else {
        // not linkclicked
        if (isBusy('loadFromHash')) {
          // we save the link, and it has to be handled later in the callback
          lib.info('page busy, saving link: '+hash);
          nextLink = hash;
          return;
        }
        menu.set(hash,'clearallselections');
        setBusy('loadFromHash');
        hashHandled = function () {
          notBusy('loadFromHash: '+hash);
          var h = nextLink;
          nextLink = undefined;
          if (typeof(h) !== 'undefined') {
            lib.info('found formerly suppressed link, handle now: '+h);
            loadFromHash(h);
          }
        }
      // not a link, this means backbutton or something
      }
      // we get the hash in the form: buch
      lib.info('loadFromHash: '+hash +' triggered by link:'+triggeredByLink);
      if (!hash) {
        lib.info('loading empty hash');
        // this happens if you go back to the first page with the back button
        // for example
        var pathname = window.location.pathname;
        lib.info('using pathname instead: '+pathname);
        hash = pathname;
      // we set the menu, as this cannot happen by a link and thus
      }
      if ((hash == '/')) {
        loadContent('',triggeredByLink,hashHandled);
        return;
      }
    
      // Regex: start with a /, then go on with letters, digits and /'s, end with .html or not.
      // Captures the part without the .html and the leading /
      // This is used as param for loadcontent
      var parse=/^\/([\w\d\./-]*)(?:\.html)?$/;
      var a=parse.exec(hash);
      if (a) {
        var p1 = a[1];
        if (p1) {
          loadContent(p1,triggeredByLink,hashHandled);
        }
      } else {
        // malformed link found
        lib.info('wrong link format: '+hash);
        loadContent('/error.html',triggeredByLink,hashHandled);
      }
    //}catch (e) {
    //lib.handle(e);
    //}
    }


    function setUpHistory() {

      // Initializes the history plugin with our function.
      // This will call the function.
      if (!lib.history) {
        $.historyInit(loadFromHash);
      }
      else {
        // html5 history
        // we define the onpopstate function
        var onpopstate = function onpopstate(ev) {
          var state = ev.state;
          if (state) {
            var d = getCurrentContent();
            //          console.log('onpopstate: '+state.param +' content current : '+d);
            loadFromHash(state.param);
            if (state.param == '/') {
              // we came back to start page via history
              // Clear the selections in the menus
              $.fn.accmenu.set('','clear');
            } else {
              // set the state in the menus
              $.fn.accmenu.set(state.param, 'clear');
            //$.fn.navileft.set(state.param, 'clear');
            }
          } else {
            //          console.log('onpopstate with state=null');
            loadFromHash('');
            // Clear the selections in the menus
            $.fn.accmenu.set('','clearselections');
          }
        }
        if (lib.history) {
          onpopstate = onpopstate;
        }
      }

      if (lib.history) {
        historyLoad = function historyLoad(hash) {
          // using html5 history
          var res = loadFromHash(hash);
          //console.log('result from loadFromHash('+hash+')'+res);
          var state = {
            param : hash
          };
          window.history.pushState(state,document.title,hash);
        }
      } else {
        // use the history plugin
        historyLoad = $.historyLoad;
      }
    }
    function setUpFFHistory() {
      lib.ffhistory.init(loadFromHash,lib.tracker);
      historyLoad = lib.ffhistory.historyLoad;
    }

    // handles all clicks on the page (except on centerview.close)
    var handleClick = function handleClick(e) {
      //    console.log(e.button);
      if (isBusy('handleClick')) {
        e.preventDefault();
        return false;
      }
      if (e.button !== 0)
      {
        return true;
      }
      var target = $(e.target);
      var a;
      if (target.is('a')) {
        a = target;
      }
      var parentA = target.parents('a');
      if (parentA.length) {
        a = parentA.eq(0);
      }
      if (a) {
        if (handleLink(a)) {
          // if the framework already handled the link, we suppress the browser default action
          e.preventDefault();
          return false;
        }
      }
      // now handle clicks which are not on links
      // not active at the moment, could be used to close the centerview
      //      if ($.fn.centerview.visible()) {
      //        if (!$(target).parents('#centerview-div').length) {}
      //      }
      return true; // give control back to browser
    }


    //  checks if the given href is a local anchor
    //  if so, returns the anchor including the #
    var getlocalhash = function getlocalhash(href) {
      if (!href) return undefined;
      // if it starts with a #, it is obviously local
      if (href.match(/^#/)) {
        return href;
      }
      // if not, it may be a local href which start with http://ourdomain.com
      // some browsers do this to links
      var start = window.location.href;
      start = start.replace(/#.*$/,'');
      //console.log(start);
      var r = href.indexOf(start);
      if (r < 0) {
        // the link does not start with http://ourdomain.com
        // so it is considered a non-local link
        return undefined;
      }
      // if we are here, we know that the link points to our domain
    
      // strip of only the hash part
      var regex = /(#.*$)/;
      var a = regex.exec(href);
      if (a) {
        return a[1];
      }

      return undefined;
    };

    // checks if the link is a local link
    // this must begin with /
    // if the given href begins with the protocol and our own domain, these are stripped of,
    // so that we always get back only the local link
    var getlocallink = function (href) {
      if (!href) return undefined;
      // matches everything beginning with /
      if (href.match(/^\/(.*)?$/)) {
        return href;
      }
      var start = window.location.protocol + '//' + window.location.host;
      var r = href.indexOf(start);
      if (r < 0) {
        return undefined;
      }
      href = href.slice(r+start.length);
      return href;
    }

    var slidein;
    function setUpSlidein() {
      if (ios) {

        slidein = function slideinMobile(cont,newcont,direction, aftercopy,cb) {
          centerview.transition.slidein(cont,newcont,direction,aftercopy,cb);
        }
      } else {
        // not ios
        slidein = function slideinDesktop(cont,newcont,direction, aftercopy,cb) {
          if (direction !== 'fromleft') {
            direction = 'fromright';
          }
          menu.hideIfSmall();
          // wrap content in div
          cont.wrap('<div id = "wrapper" style="position:relative;"/>');
          var $wrap = cont.parent();
          // save parent scrollbar css
          var $parent = $wrap.parent();
          var overflow = $parent.css('overflow');
          var overflowx = $parent.css('overflow-x');
          var overflowy = $parent.css('overflow-y');
          // hide parent scrollbars
          $parent.css('overflow', 'hidden');
          //$wrap.parent().css('overflow', 'auto');
          var $oldcont;
          // define the callback function when the slide is over
          var slideready = function() {
            // remove old content
            $oldcont.remove();
            // restore parent scrollbar settings
            $wrap.parent().css({
              overflow : overflow,
              overflowX : overflowx,
              overflowY : overflowy
            });
            $wrap.find('.unhide').css('visibility','visible');
            // remove wrapper
            cont.unwrap();
            cont.css({
              position: 'static',
              left : 0
            });
            cb();
          }

          if (direction === 'fromright') {
            // add the div for the old content at the left
            cont.before('<div style="float: left"/>');
            $oldcont = cont.prev();
            $oldcont.html(cont.html());
            cont.html(newcont.html());
            // the content may need some work, e.g. restoring event handlers:
            aftercopy();
            // move content to the right
            cont.css({
              position: 'absolute',
              left : '960px',
              top : 0
            });
            // hide the iframes and stuff again
            // and slide back in
            $wrap.find('.unhide').css('visibility','hidden');
            $wrap.animate({
              left : -960
            },
            500,
            'easeOutQuad',
            slideready);
          } else {
            // slide in from left
            // add the div for the old content at the right
            cont.after('<div style="float: right"/>');
            $oldcont = cont.next();
            // copy the current content to oldcont and the new to cont
            $oldcont.html(cont.html());
            cont.html(newcont.html());
            aftercopy();
            // move the new content to the left
            cont.css({
              position: 'absolute',
              left : '-960px',
              top : 0
            });
            //      $wrap.css({
            //        left : '0px'
            //      });
            // and slide back in to the right
            $wrap.animate({
              left : 960
            },
            500,
            'easeOutQuad',
            slideready);
          }
        }
      }
    }

    function handleLinkExt(ext,triggeredByLink,cb) {
      if (isExtHandled(ext)) {
        lib.info('ext handling cancelled, handled already: '+ext);
        cb();
        return;
      }
      // expects something like #team
      // if div#team exists, the handleLocalHash is called
      // else if a#team exists, the appropriate action is taken
      var divs = $('div'+ext);
      var res = false;
      if (divs.length) {
        // we have a div with this id
        res = handleLocalHash(ext,cb);
      } else {
        // look for an <a> with this id
        var as = $('a'+ext);
        if (as.length) {
          var a = $(as[0]);
          res = clickLinkFromExt(a, triggeredByLink,cb);
        } else {
          lib.warn('did not find <a> with '+ext);
          res = true;
        }
      }
      if (res) {
        // the ext has been handled successfully, save this to the content, to prevent the
        // ext handling from happening twice
        setExtHandled(ext);
      }
    }

    function clickLinkFromExt(a,triggeredByLink,cb) {
      // we have to simulate clicking on a link marked by a link extension
      var href = a.attr('href');
      var rel = a.attr('rel');
      var cviframe = a.attr('cviframe');

      // we have to handle these types:

      // * local links with an url in the rel attribute
      //   load the rel to the cv and show it
      // * external links with rel=cv
      //   show an iframe with the content of the url
      // * link with a cviframe attribute
      //   show an iframe with the content of the url in cviframe, ignore href

      if (rel && (rel !== 'cv')) {
        // we got a rel link
        return handleRelLink(rel,cb);
      }

      // check if it is al local link
      // this would make no sense
      var locallink = getlocallink(href);
      if (locallink) {
        lib.error('error: link extension pointing to local link: '+href);
      }

      var handled = handleExternalLink(href, rel, cviframe,cb);
      if (!handled) {
        if (ios) {
          // as ios does not handle the local link, we move the browser to the new location
          if (triggeredByLink) {
            // as the change to this hash was triggered by the user clicking on a link,
            // we can assume that he wants to change here
            window.location = href;
            return true;
          }
          var res = confirm('Zu dieser Seite wechseln?\n'+href);
          if (res) {
            window.location = href;
          } else {
            return true; // the ext is handled
          }

        } else {
          // not ios
          // on ios external links return false, to enable browser default handling

          lib.error('error: link from link extension could not be handled: '+href);
        }
        return false;
      } else {
        return true;
      }
    }

    // NOT the history callback function, really just handles links like #team
    function handleLocalHash(localhash,afterHandling) {
      lib.info('handleLocalHash: '+localhash);
      if (localhash === '#') {
        // the anchor is empty, if there is a rel instead
        // we use that as link
        afterHandling();
        return true;
      }
      // check if the centerview is visible already
      if (normalCvVisible()) {
        // it is visible, so we slide in the content
        var cont = $cvcont;
        var newcont = $(localhash);

        // find the right direction to slide in from
        var oldindex = $cvcont.data('index');
        var newindex = newcont.index();
        var direction = 'fromright';
        if (newindex < oldindex) {
          direction = 'fromleft';
        }
        // save the index in the cv content div
        $cvcont.data('index',newcont.index());
        $cvcont.data('url',localhash);
        var cb = function () {
          $cvdiv.scrollTop(0);
          afterHandling();
        }
        slidein(cont,newcont,direction,aftercopy,cb);
      } else {
        // the cv is not yet visible
        // copy the content of the div with the given hash to the centerview div
        var $newcv = $(localhash);
        $cvcont.html($newcv.html());
        aftercopy();
        // save the index of the data to get the scroll direction later on
        $cvcont.data('index', $newcv.index() );
        $cvcont.data('url',localhash);
        showcv(function () {
          $cvdiv.scrollTop(0);
          afterHandling();
        });
      }
      return true;
    }

    var handleRelLink = function handleRelLink(rel,afterHandling) {
      // if there is a link in rel, we use this instead of the href arg
      // the href can be used as a fallback for no js
      // we load the content of the rel to the centerview using ajax
      //console.log('using the rel attr as href:'+rel);
      var oldsrc = $cvcont.data('url');
      if (rel === oldsrc) {
        // the content of the url is already in the centerview
        if (!normalCvVisible()) {
          showcv(function () {
            $cvdiv.scrollTop(0);
            afterHandling();
          });
        } else {
          // we have already the normal cv with the correct content, so not much to do here
          afterHandling();
        }
        return true;
      }

      // ajax success handler
      var success = function (html) {
        // remove stylesheet links
        html = removeComment(html);
        // Each content page should start with a special comment (see: docs/readme.txt)
        // We extract the metainfo in this comment here
        var re=/^\s*<!--([\s\r\n\w:,]*)-->/;
        html = html.replace(re,'');
        // check if the cv is visible already
        // check if the centerview is visible already

        if (normalCvVisible()) {
          // it is visible, so we slide in the content

          // copy the html to the page
          $ajaxcontent.html(html);
          disableIFrames($ajaxcontent);
          // we always slide in from right
          var direction = 'fromright';
          $cvcont.data('index',100);
          $cvcont.data('url',rel);
          slidein($cvcont,$ajaxcontent,direction,aftercopy,afterHandling);
        } else {
          // cv is not visible
          $cvcont.html(html);
          aftercopy();
          $cvcont.data('index',100);
          $cvcont.data('url',rel);
          showcv(function () {
            $cvdiv.scrollTop(0);
            afterHandling();
          });
        }
        lib.initKuenform();
      }
      // load the content via ajax
      $.ajax(
      {
        dataType: 'html',
        url: rel,
        success: success,
        error: function (request, status, error) {
          // load error page
          // alert('could not load:'+rel);
          afterHandling();
          loadContent('error');
        }
      });
      return true; // we handled the link with the ajax call
    }

    function handleExternalLink(href,rel,cviframe,cb) {
      // returns true if the link is handled, false otherwise

      if (ios) {
        cb();
        return false;
      }
    
      // it might still have to be displayed in the centerview as an iframe
      // if the rel attribute is cv
      if (rel === 'cv') {
        //lib.info('loading external link to centerview:' +href);
        centerview.iframe({},href,cb);
        return true;
      }

      // we have an external link, and no rel attribute
      // we check if the attribute 'cviframe' is present instead, and use this as an iframe cv src instead of the href
      if (cviframe) {
        //console.log('using cviframe attribute to load cv as iframe: '+cviframe);
        centerview.iframe({},cviframe,cb);
        return true;
      }

      // we could not do something framework-specific with the link
      // so we return false, which will result in the default link handling of the webbrowser
      lib.info("Could not handle link: "+href);
      cb();
      return false;
    }

    // ################### handleLink ##########################
    // The central 'link-hijacking' function.
    // Tries to do something with the link via ajax.
    // Returns true if the link is handled,
    // False if it was not possible to handle the link.
    // #########################################################
    var handleLink = function handleLink(link) {
      // variables used function-wide
      var href = link.attr('href');
      var rel = link.attr('rel');
      var cviframe = link.attr('cviframe');
      //alert('handle link: '+href);

      setBusy('handleLink');
      var afterHandling = function () {
        notBusy('afterHandling ' + href);
      }
      // check for links to social bookmaks from 'addthis'
      // these start with // and shall not be treated by the framework
      if (href && href.slice(0,2) == '//') {
        // do not handle link
        afterHandling();
        return false;
      }

      if (!href) {
        afterHandling();
        return false;
      }

      // if there is a rel attribute, this shall override the href link
      // rel=cv is handled later, as it is only valid for external links
      if (rel && (rel !== 'cv')) {
        return handleRelLink(rel,afterHandling);
      }

      // first check if href points to a local anchor
      // these shall be loaded in the centerview div and displayed
      var localhash = getlocalhash(href);
      if (localhash) {
        return handleLocalHash(localhash,afterHandling);
      }

      // now we check if it is a local link, to our actual domain
      var locallink = getlocallink(href)
      if (locallink) {
        //alert('handling local link:'+locallink)
        if (rel === 'cv') {
          lib.warn('local link should not have rel=cv');
        }
        // it is a local link, which we will handle via ajax

        // we mark the fact, that we did
        lib.info('setting linkclickedCallback');
        //linkclicked = true;
        // save the callbakc, so that it can be executed in loadFromHash
        linkclickedCallback = afterHandling;
        // when suppliing the same link twice
        historyLoad(locallink);

        // if the link came from the menu, the nav path is set already
        // but if it came from elsewhere, we should try to set the pastth
        if (!link.parents('#nav').length){
          menu.set(locallink,'clearallselections');
        }
        return true;
      }

      // it is an external link
      return handleExternalLink(href, rel, cviframe,afterHandling);
    }

    function toggleNavDir() {
      if (hsp.navioben) {
        hsp.navioben = false;
        $.fn.accmenu.toggleElem = $navToggle;
        $('html').removeClass('navioben');
        $.fn.accmenu.toggleHeight = $navToggle.height();
        $('#nav-wrapper').height($navToggle.height());
      } else {
        hsp.navioben = true;
        $.fn.accmenu.toggleHeight = 0;
        $.fn.accmenu.toggleElem = $toggleOben;
        $('html').addClass('navioben');
      }
      $.fn.accmenu.vonoben = hsp.navioben;
    }
    // now, at the end of document.ready call all initialization functions
    initPage();
  } catch(e) {
    lib.handle(e);
  }
}); // document.ready

// add preloading
(function($) {
  var cache = [];
  // Arguments are image paths relative to the current page.
  $.preLoadImages = function() {
    var args_len = arguments.length;
    for (var i = args_len; i--;) {
      var cacheImage = document.createElement('img');
      cacheImage.src = arguments[i];
      cache.push(cacheImage);
    }
  }
})(jQuery) // pass in the jQuery variable as $, to allow renaming

jQuery.preLoadImages("/zentral/x-common/img/navizentrale-links-schliessen.gif",
  "/zentral/x-common/img/navizentrale-links-oeffnen.gif");

