
var site = {

  raw:              null,
  data:             null,
  translation:      null,

  pollingInterval:  100,
  timerCookie:      null,
  currentHash:      null,
  currentLocale:    null,
  scrollPositions:  { },

  contentSelector:  "#contents",

  /**
   * This function loads the site model via an asynchon Ajax request. After successful
   * loading, the 'home' page is shown.
   *
   * @param contentUrl The url to the corresponding json file.
   */
  load: function() {
      var contentUrl = 'site.json';
      var myUrl = window.location.href;

      myUrl.replace(/content=([^;#&]+)/,
                    function (match, name) {
                        contentUrl = name;
                    });

      var locale = navigator.language || navigator.userLanguage;
      document.cookie.replace(/.*\blang=(en|de)\b/,
                              function (match, language) {
                                  locale = language;
                              });
      if (locale != 'de' && locale != 'en') {
          locale = 'en';
      }

      var context = this;

      var request = new Request(
          {
              url: '/cgi-bin/get-database.pl/' + contentUrl,
              method: 'get',
              onSuccess: function(content) {
                  context.data = JSON.decode(content, true);
                  context.init(locale);
              },
              onFailure: function(xhr) {
                  alert('error while loading site data!');
              }
          });
      request.get();

      // start location hash polling ...
      this.timerCookie = window.setTimeout(this.onTick.bind(this), this.pollingInterval);
  },

    /**
     * Reinitializes the site in a different language, storing the choice in a cookie
     */
    switchLanguage: function(language) {
        document.cookie = "lang=" + language;
        this.init(language);
    },

  /**
   * Inits the site with the already loaded raw data and the given
   * locale. Afterwards navigate to the start page, which is either the
   * page referenced in the anchor or the home page.
   *
   * @param locale the locale, the site should be init with
   */
  init: function(locale) {
      this.setLocale(locale);
      this.make_menu();
      this.initLayout();
      this.navigate(window.location.hash.replace(/^#/, '') || 'page/home');
  },

  /**
   * Sets the current locale and selects the correct link.
   *
   * @param value the locale to select
   */
  setLocale: function(value) {
    this.currentLocale = value;

    $$("#language a").removeClass("selected");
    $$("#language a." + value).addClass("selected");
  },

  initLayout: function() {
    var projectCount = jsonPath(this.data, '$..projects[?( @.onHomePage && isProjectPublished(@) )]').length;
    var curatorProjectCount = jsonPath(this.data, '$..curator[?( @.onHomePage && isProjectPublished(@) )]').length;
    layoutStore.resetPositions(projectCount + curatorProjectCount);
  },

  /**
   * This function returns the text for the given key and the current lcoale.
   *
   * @param key the key of text to get
   */
  getText: function(key) {
    return this.translation[this.currentLocale][key];
  },

  /**
   * This function uses a template and a base data object to generate some boxes
   * inside the content area of the site. The kind, content and position of that
   * boxes is defined inside the templateStore. The member value contentSelector
   * is used to identify the container div for the content.
   *
   * @param templateName  The template name defined inside the templateStore.
   * @param dataPath      The jsonPath to the base data object.
   */
  show: function(templateName, dataPath) {
    // find template
    var template = templateStore[templateName];
    if (!template) { alert('template ' + templateName + ' couldn\'t be found'); return; }

    // find data
    var data = dataPath ? (typeof(dataPath) == 'string' ? jsonPath(this.data, dataPath) : dataPath) : [ this.data ];

    // find content container
    var content = $$(this.contentSelector);

    // clear the content area
    content.set('html', null);

    // create array of elements for the page
    var boxes = [ ];
    template.templates.each(function(template) {
      var object = eval('new ' + template['class'] + '(data, template)');

      // if the object provides a collection, add each member to the current container
      if (object.toCollection)
        object.toCollection().each(function(member) {
          if (member.toElement)
            boxes.push(member);
        });

      // just add object that provides the toElement method
      if (object.toElement)
        boxes.push(object);
    });

    boxes.each(function(box) {
      content.grab(box);
    });

    // layout objects
    if (template.layout) {
      layoutStore.calculateNumberOfStairColumns(content.getComputedStyle('width'));
      layoutStore.layout(template.layout, boxes);

      $$('#language').setStyle('left', (110 + layoutStore.layoutWidth()) + "px");
    }
  },

    menu_defs: [
        { 'class': 'page', path: 'page/home', id: 'home', title: 'Home' },
        { 'class': 'category', path: 'category/radioart', id: 'radioart',
          title: { de: 'Radiokunst', en: 'Radio Art' } },
        { 'class': 'category', path: 'category/electroacoustics', id: 'electroacoustics',
          title: { de: 'Elektroakustik', en: 'Electro Acoustics' } },
        { 'class': 'category', path: 'category/soundplastics', id: 'soundplastics',
          title: { de: 'Klangplastik', en: 'Sound Plastic' } },
        { 'class': 'category', path: 'category/urban_environment', id: 'urban_environment',
          title: 'Urban Environment' },
        { 'class': 'category', path: 'category/musictheater', id: 'musictheater',
          title: { de: 'Musiktheater', en: 'Music Theatre' } },
        { 'class': 'category', path: 'category/instrumental_vocal', id: 'instrumental_vocal',
          title: { de: 'Instrumental Vokal', en: 'Instrumental Vocal' } },
        { 'class': 'category', path: 'category/performance', id: 'performance',
          title: 'Performance' },
        { 'class': 'category', path: 'category/notation', id: 'notation',
          title: { de: 'Notation', en: 'Notation' } },
        { 'class': 'page', path: 'page/curator', id: 'curator',
          title: { de: 'Kurator', en: 'Curator' } },
        { 'class': 'page', path: 'page/texts', id: 'texts',
          title: { de: 'Texte', en: 'Texts' } },
        { 'class': 'page', path: 'page/cd_dvds', id: 'cd_dvds',
          title: 'CD / DVD' },
        { 'class': 'page', path: 'page/works', id: 'works',
          title: { de: 'Werke', en: 'Works' } },
        { 'class': 'page', path: 'page/personalia', id: 'personalia',
          title: 'Personalia' },
        { 'class': 'page', path: 'page/contact', id: 'contact',
          title: { de: 'Kontakt', en: 'Contact' } }
    ],

    make_menu: function () {
        replaceChildNodes($$('#menu')[0],
                          UL(null,
                             map(function (entry) {
                                     return LI({ 'class': entry['class'] },
                                               A({ href: 'javascript:site.navigate("' + entry.path + '");',
                                                   id: entry.id },
                                                 translate(entry.title)));
                                 }, this.menu_defs)));
    },

    show_project: function (project_name) {
      var category = this.projectCategory(project_name);
      var projectsInCategory;
      var index;

      projectsInCategory = jsonPath(this.data,
        category == "curator" ?
          "$.curator[?( isProjectPublished(@) )]" :
          "$.categories[?(@.name == '" + category + "')].projects[?( isProjectPublished(@) )]");

      index = findValue(map(function (project) { return project.name; }, projectsInCategory), project_name);

      if (index > 0) {
        $$('#projnavi .previous')
          .setStyle('visibility', 'visible')
            .setProperty('href', 'javascript:site.navigate("project/' + projectsInCategory[index - 1].name + '");');
      }
      if (index < (projectsInCategory.length - 1)) {
        $$('#projnavi .next')
          .setStyle('visibility', 'visible')
            .setProperty('href', 'javascript:site.navigate("project/' + projectsInCategory[index + 1].name + '");');
      }

      this.show('project', [ projectsInCategory[index] ]);
      this.selectLink(category);
    },


    /**
     * This function takes a route and navigate to the requested page. Via a special hack
     * the browser histroy will be keept.
     *
     * @param route The route to a page.
     */
    navigate: function(route) {
        var pattern = /^(.*)\/(.*)$/;
        var result = pattern.exec(route ? route : 'page/home');
        if (!result) return;

        // save scroll position
        if (this.currentHash) {
          // this.scrollPositions[this.currentHash] = this.getScrollXY();
        }

        this.currentHash = '#' + route;
        window.location.hash = route;

        $$('#projnavi a').setStyle('visibility', 'hidden');

        switch (result[1]) {
        case 'page':
            switch (result[2]) {
            case 'home':        this.show('home', false); break;
            case 'curator':     this.show('curator', '$.curator[?( isProjectPublished(@) )]'); break;
            case 'texts':       this.show('gallery', '$.texts[?(@.title.' + this.currentLocale + ' != "")]'); break;
            case 'cd_dvds':     this.show('gallery', '$.cd_dvds[?(@.title.' + this.currentLocale + ' != "")]'); break;
            case 'works':       this.show('works', false); break;
            case 'personalia':  this.show('personalia', '$.personalia'); break;
            case 'contact':     this.show('contact', '$.contact'); break;
            case 'events':      this.show('events', false); break;
            }
            this.selectLink(result[2]);
            break;

        case 'category':
            this.show('category', '$.categories[?(@.name==\'' + result[2] + '\')].projects[?( isProjectPublished(@) )]');
            this.selectLink(result[2]);
            break;

        case 'project':
            this.show_project(result[2]);
            break;

        }

        var scrollPosition = this.scrollPositions[this.currentHash];
        if (scrollPosition) {
          window.scroll(scrollPosition.x, scrollPosition.y);
        } else {
          window.scroll(0, 0);
        }
    },

  getScrollXY: function() {
    var x = 0, y = 0;
    if (typeof(window.pageYOffset) == 'number') {
      // Netscape compliant
      y = window.pageYOffset;
      x = window.pageXOffset;
    } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
      // DOM compliant
      y = document.body.scrollTop;
      x = document.body.scrollLeft;
    } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
      // IE6 standards compliant mode
      y = document.documentElement.scrollTop;
      x = document.documentElement.scrollLeft;
    }
    return { x: x, y: y };
  },

  projectCategory: function(projectName) {
    if (projectName.match(/^r/))  return 'radioart';
    if (projectName.match(/^e/))  return 'electroacoustics';
    if (projectName.match(/^kk/)) return 'soundplastics';
    if (projectName.match(/^u/))  return 'urban_environment';
    if (projectName.match(/^m/))  return 'musictheater';
    if (projectName.match(/^iv/)) return 'instrumental_vocal';
    if (projectName.match(/^p/))  return 'performance';
    if (projectName.match(/^n/))  return 'notation';
    if (projectName.match(/^c/))  return 'curator';
    return false;
  },

  selectLink: function(id) {
      /* link im menu hervorheben */
      log('selectLink: ' + id);
    if (this.selectedLink)
      this.selectedLink.removeClass('selected');

    this.selectedLink = $$('#' + id);
    this.selectedLink.addClass('selected');
  },

  onTick: function() {
      this.timerCookie = null;

      try {
          if (this.data && this.currentHash != window.location.hash)
              this.navigate(window.location.hash.replace(/^#/, ''));
      }
      catch (e) {
          /* ignore possible errors to make sure that our timer is re-established */
      }

      this.timerCookie = window.setTimeout(this.onTick.bind(this), this.pollingInterval);
  }

};

var static_strings = {
    en: { 'mehr': 'more' }
};

function translate(content) {
    if (typeof(content) == 'string') {
        if (static_strings[site.currentLocale] && static_strings[site.currentLocale][content]) {
            return static_strings[site.currentLocale][content];
        } else {
            return content;
        }
    } else {
        return content[site.currentLocale];
    }
}

function isProjectPublished(project) {
  return project.published_locales && project.published_locales.contains(site.currentLocale);
}

if (navigator.userAgent && navigator.userAgent.match(/Safari\/(\d+)/)) {
    if (RegExp.$1 < 522) { // Safari 3 started with version 522.11
        alert("This website does not support Safari version 2, please upgrade or "
              + "use another web browser (z.B. Firefox)\n\n"
              + "Diese Website funktioniert nicht mit Safari version 2, bitte "
              + "installieren Sie eine neuere Version oder verwenden Sie einen "
              + "anderen Web-Browser (z.B. Firefox)");
        site.load = function () {};
    }
}

