/* Pages navigation.
 * 
 * Copyright (c) 2010 Raphaël Bois
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software  and associated  documentation  files (the  "Software"), to
 * deal in the Software without  restriction, including  without limitation the
 * rights to use, copy, modify, merge,  publish, distribute, sublicense, and/or
 * sell copies of the Software,  and to permit persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice  and this permission notice  shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED  "AS IS", WITHOUT WARRANTY  OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE  WARRANTIES OF  MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR  COPYRIGHT  HOLDERS BE  LIABLE FOR  ANY CLAIM,  DAMAGES  OR OTHER
 * LIABILITY,  WHETHER IN AN  ACTION OF  CONTRACT, TORT  OR OTHERWISE,  ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 * 
 * page-nav.js
 */

try {
  if (!MooTools || MooTools.version != '1.2.4')
    throw "!!!";
} catch (e) { throw "This module requires MooTools 1.2.4"; }

var PageNav = new Class({
  Implements: [Options, Events],
  Binds: [
    'pageAdd', 'pageMerge', 'pageGet', 'pageQueueRequest', 'pageShow', 'pageLoad', 'onPageLoaded',
    'scanList', 'scanEntry',
    'activeMenu', 'activePage', 'navUpdate'
  ],
  options: {
    'loading': null, /* Object to show when loading pages. */
    'permalink': null /* anchor presenting the permalink of the active page. */
  },
  initialize: function (menu_root, container, options) {
    this.setOptions(options);
    this.mnuRoot = $(menu_root);
    this.container = $(container);
    this.activeStack = [];
    this.pageActive = 0;
    this.pageFading = 0;
    this.showNext = 0;
    this.requestQueue = [];
    this.requestPage = 0;
    this.showOnLoad = 0;
    this.pages = {};
    this.pageReq = new Request.JSON({
      'method': 'post',
      'url': 'page.php',
      'link': 'ignore',
      'nocache': 'true'
    });
    var f = function () {
      this.requestPage = this.requestQueue.shift();
      this.pageLoad(this.requestPage);
    }.bind(this);
    this.pageReq.addEvents({
      'success': function (data, txt) {
        this.onPageLoaded(data, txt);
        this.requestPage = this.requestQueue.shift();
        this.pageLoad(this.requestPage);
      }.bind(this),
      'cancel': f,
      'failure': f,
    });
    if (this.mnuRoot) {
      this.scanList(this.mnuRoot);
    }
    this.nextOver = new Element('a', {'href':'#', 'class': 'nav-over nav-next'}).inject(this.container, 'top');
    this.next = [
      /* new Element('a', {'href':'#', 'class': 'page-nav-next'}).inject(this.container, 'top'), */
      this.nextOver
    ];
    /* this.prev = (new Element('a', {'href':'#', 'class': 'page-nav-prev'})).inject(this.container, 'top'); */
    this.prevOver = new Element('a', {'href':'#', 'class': 'nav-over nav-prev'}).inject(this.container, 'top');
    this.prev = [
      /* new Element('a', {'href':'#', 'class': 'page-nav-prev'}).inject(this.container, 'top'), */
      this.prevOver
    ];
    this.prev.each(function (e) {
      e.addEvent('click', function (e) {
        var p = this.pageGet(this.pageActive);
        if (p && p.prevPage > 0) {
          this.activePage(p.prevPage);
        }
        e.stop();
      }.bind(this));
    }.bind(this));
    this.next.each(function (e) {
      e.addEvent('click', function (e) {
        var p = this.pageGet(this.pageActive);
        if (p && p.nextPage > 0) {
          this.activePage(p.nextPage);
        }
        e.stop();
      }.bind(this));
    }.bind(this));
    this.dock = new Element('div', {styles:{'color':'#fff', 'position':'fixed', 'left':10, 'top':10}});
    this.dock.inject(this.container);
    
    [this.nextOver, this.prevOver].each(function (e) {
      var m = e.set('morph', {'duration':200, 'fps':30, 'link': 'cancel',
        onComplete: function () {
          if (this.getStyle('opacity') == 0) {
            this.setStyle('visibility', 'hidden');
          } else {
            this.setStyle('visibility', 'visible');
          }
        }.bind(e),
        onStart: function () {
          this.setStyle('visibility', 'visible');
        }.bind(e)
      });
      m.set('opacity', 0.0);
    });

    this.container.addEvent('mousemove', function (event) {
      var p = this.container.getPosition();
      var x = this.container.getSize().x;
      if (event.page.x - p.x < 0.33 * x) {
        this.prevOver.morph({'opacity': 1.0});
      } else if (this.prevOver.getStyle('visibility') == 'visible') {
        this.prevOver.morph({'opacity': 0.0});
      }
      /* this.nextOver.setStyle('visibility', event.page.x - p.x > 0.67 * x ? 'visible' : 'hidden'); */
      if (event.page.x - p.x > 0.67 * x) {
        this.nextOver.morph({'opacity': 1.0});
      } else if (this.nextOver.getStyle('visibility') == 'visible') {
        this.nextOver.morph({'opacity': 0.0});
      }
      var y = Math.max(this.prevOver.getSize().y, this.nextOver.getSize().y);
      /* this.dock.set('html', 'X='+event.page.x + ' - Y='+event.page.y); */
      var pos = Math.max(event.page.y - p.y - y / 2);
      this.prevOver.setStyle('top', pos);
      this.nextOver.setStyle('top', pos);
    }.bind(this));
    this.container.addEvent('mouseleave', function () {
      if (this.prevOver.getStyle('visibility') == 'visible') {
        this.prevOver.morph({'opacity':0.0});
      }
      if (this.nextOver.getStyle('visibility') == 'visible') {
        this.nextOver.morph({'opacity':0.0});
      }
    }.bind(this));
    if (this.pageActive) { /* Get page infos. */
      this.pageLoad.delay(50, this, [this.pageActive, true]);
    }
  },
  pageAdd: function (page, infos) {
    if (!infos) infos = {};
    var pinfos = $extend({'page':page,'prevPage':0,'nextPage':0}, infos);
    this.pages['p'+page] = pinfos
    return pinfos;
  },
  pageMerge: function (page, infos) {
    var pinfos = this.pageGet(page);
    if (!pinfos) {
      pinfos = this.pageAdd(page, infos);
    } else if (infos) {
      $extend(pinfos, infos);
    }
    return pinfos;
  },
  pageGet: function (page) {
    return this.pages['p'+page];
  },

  scanList: function (ul) {
    ul = $(ul);
    if (!ul) { return; }
    if (ul.hasClass('expandable')) {
      ul.set('reveal', {
        'fps':25,
        'link':'chain',
        'duration':300,
        'transition': 'cubic:out'
      });
    }
    var lis = ul.getChildren('li');
    if (lis && lis.length) {
      lis.each(this.scanEntry);
    }
  },
  scanEntry: function (li) {
    li = $(li); /* Required in old IE */
    if (li.hasClass('active')) {
      this.activeStack.push(li);
    }
    var subul = li.getFirst('ul');
    var a = li.getFirst('div');
    if (a) a = a.getFirst('a');
    if (!a) { return; }
    if (a.hasClass('directlink')) {
      a.set('target', '_blank');
      return;
    }
    var page = a.get('href').replace('?page=','').toInt();
    if (a.hasClass('active')) {
      this.pageActive = page;
    }
    if (!li.hasClass('project') || !li.getFirst('ul')) {
      this.pageAdd(page, {'li':li});
    }
    a.addEvent('click', function (e) {
      this.n.activePage(this.page);
      if (e) e.stop();
      return false; /* Prevents link from being activated. */
    }.bind({n:this, page:page}));
    this.scanList(subul);
  },

  activeMenu: function (page) {
    if (page == this.activePage) return;
    var pinfos = this.pageGet(page) || {};
    var li = pinfos.li;
    if (li) {
      var a = li.getFirst('div').getFirst('a');
      this.activeStack.getLast().getFirst('div').getFirst('a').removeClass('active');
      a.addClass('active');
      var newStack = [li];
      var ul = li.getParent();
      while (ul && ul != this.mnuRoot) {
        li = ul.getParent();
        newStack.unshift(li);
        ul = li.getParent();
      }
      var i = 0;
      for (; i < this.activeStack.length || i < newStack.length; i++) {
        if (i < newStack.length) {
          if (i < this.activeStack.length && this.activeStack[i] == newStack[i]) {
            continue;
          }
          /* newStack[i].addClass('active'); */
          ul = newStack[i].getFirst('ul');
          if (ul && ul.hasClass('expandable')) {
            ul.setStyle('visibility','visible');
            ul.reveal();
          }
        }
        if (i < this.activeStack.length) {
          ul = this.activeStack[i].getFirst('ul');
          if (ul && ul.hasClass('expandable')) { /* Override default CSS behaviour. */
            ul.setStyles({
              'display': 'block',
              'visibility': 'visible'
            });
            ul.dissolve();
          }
          this.activeStack[i].removeClass('active'); /* May remain from the original init. */
        }
      }
      this.activeStack = newStack;
    }
  },
  activePage: function (page) {
    if (page == this.activePage) return;
    this.activeMenu(page);
    var pinfos = this.pageGet(page);
    var p = $('page-'+page);
    if (!p) {
      this.showOnLoad = page;
      this.pageQueueRequest(page, true);
    } else {
      this.showOnLoad = 0; /* Prevent any previously loaded page to show after that */
      this.pageQueueRequest(0, true); /* clear pending requests. */
      this.pageShow(page);
    }
  },
  pageShow: function (page) {
    if (page == this.pageActive) return;
    if (this.pageFading > 0) {
      this.showNext = page;
      return;
    }
    this.pageFading = this.pageActive;
    this.pageActive = page;
    var op = $('page-'+this.pageFading);
    var p = $('page-'+page);
    var pinfos = this.pageGet(page);
    if (op) op.setStyle('z-index', 1);
    p.setStyles({'visibility': 'visible', 'z-index': 2, 'opacity':0.0});
    if (!pinfos.morphIsSet) {
      var m = p.set('morph', {'fps':25,'duration':500, 'transition':'linear'}).get('morph');
      m.addEvent('complete', function () {
        if (this.pageFading > 0) {
          var p = $('page-'+this.pageFading);
          if (p) p.setStyles({'visibility':'hidden', 'z-index': 0});
        }
        this.pageFading = -1;
        if (this.showNext > 0) {
          var n = this.showNext;
          this.showNext = -1;
          this.pageShow(n);
        }
      }.bind(this));
      pinfos.morphIsSet = true;
    }
    p.morph({'opacity':[0.0,1.0]});
    this.navUpdate();
  },
  pageQueueRequest: function (page, clearQueue) {
    if (clearQueue) this.requestQueue.empty();
    if (!page) return;
    this.requestQueue.push(page);
    if (!this.requestPage) {
      this.requestPage = this.requestQueue.shift();
      this.pageLoad(this.requestPage);
    }
  },
  navUpdate: function () {
    var pi = this.pageGet(this.pageActive);
    this.prev.each(function (e) {
      e.setStyle('display', this.prevPage > 0 ? 'block' : 'none');
    }.bind(pi));
    this.next.each(function (e) {
      e.setStyle('display', pi.nextPage > 0 ? 'block' : 'none');
    }.bind(pi));
  },
  onPageLoaded: function (infos, raw) {
    if (!infos || !infos.page) return; /* Must be a valid page. */
    if ('contents' in infos) {
      if (this.container) {
        var div = new Element('div', {
          'id':'page-'+infos.page,
          'class': 'page',
          'html':infos.contents,
          'styles': {'visibility': 'hidden', 'z-index': 0}
        }).inject(this.container);
      }
      delete infos.contents;
    }
    this.pageMerge(infos.page, infos);
    if (this.showOnLoad == infos.page) {
      this.showOnLoad = 0;
      this.pageShow(infos.page);
    } else if (this.pageActive == infos.page) { /* Occurs for the first page. */
      this.navUpdate();
    }
  },
  pageLoad: function (page, infosOnly) {
    if (!page) return;
    var d = {
      'action': (infosOnly ? 'page-nav-info' : 'page-full'),
      'page': page
    };
    this.pageReq.send({data:d});
  }
});

var pageNav = null;
window.addEvent('domready', function () {
  pageNav = new PageNav('menu-cat-0', 'pages');
});


