/*
 * nyroModal - jQuery Plugin
 * http://nyromodal.nyrodev.com
 *
 * Copyright (c) 2008 Cedric Nirousset (nyrodev.com)
 * Licensed under the MIT license
 *
 * $Date: 2012/01/09 18:39:17 $
 * $version: 1.3.1
 */
jQuery(function($) {

  // -------------------------------------------------------
  // Private Variables
  // -------------------------------------------------------

  var isIE6 = ($.browser.msie && parseInt($.browser.version.substr(0,1)) < 7);
  var body = $('body');

  var currentSettings;

  // To know if the fix for the Issue 10 should be applied (or has been applied)
  var fixFF = false;

  // Used for retrieve the content from an hidden div
  var contentElt;
  var contentEltLast;

  // Contains info about nyroModal state and all div references
  var modal = {
    started: false,
    ready: false,
    dataReady: false,
    anim: false,
    loadingShown: false,
    transition: false,
    error: false,
    full: null,
    bg: null,
    loading: null,
    tmp: null,
    content: null,
    wrapper: null,
    closing: false,
    contentWrapper: null,
    scripts: new Array()
  };

  // Indicate of the height or the width was resized, to reinit the currentsettings related to null
  var resized = {
    width: false,
    height: false
  };


  // -------------------------------------------------------
  // Public function
  // -------------------------------------------------------

  // jQuery extension function. A paramater object could be used to overwrite the default settings
  $.fn.nyroModal = function(settings) {
    if (!this)
      return false;
    return this.each(function(){
      if (this.nodeName.toLowerCase() == 'form') {
        $(this).submit(function(e) {
          if (this.enctype == 'multipart/form-data') {
            processModal($.extend(settings, {
              from: this
            }));
            return true;
          }
          e.preventDefault();
          processModal($.extend(settings, {
            from: this
          }));
          return false;
        });
      } else {
        $(this).click(function(e) {
          e.preventDefault();
          processModal($.extend(settings, {
            from: this
          }));
          return false;
        });
      }
    });
  };

  // jQuery extension function to call manually the modal. A paramater object could be used to overwrite the default settings
  $.fn.nyroModalManual = function(settings) {
    if (!this.length)
      processModal(settings);
    return this.each(function(){
      processModal($.extend(settings, {
        from: this
      }));
    });
  };

  $.nyroModalManual = function(settings) {
    processModal(settings);
  };

  // Update the current settings
  // object settings
  // string deep1 first key where overwrite the settings
  // string deep2 second key where overwrite the settings
  $.nyroModalSettings = function(settings, deep1, deep2) {
    setCurrentSettings(settings, deep1, deep2);
    if (!deep1 && modal.started) {
      if (modal.bg && settings.bgColor)
        currentSettings.updateBgColor(modal, currentSettings, function(){});

      if (modal.contentWrapper && settings.title) {
        title = $('h1#nyroModalTitle', modal.contentWrapper);
        if (title.length)
          title.text(settings.title);
        else
          modal.contentWrapper.prepend('<h1 id="nyroModalTitle">'+settings.title+'</h1>');
      }

      if (modal.content && (modal.dataReady && !modal.anim && !modal.transition) && (settings.width || settings.height)) {
        calculateSize(true);

        if (fixFF)
          modal.content.css({position: ''});
        currentSettings.resize(modal, currentSettings, function() {
          if (fixFF)
            modal.content.css({position: 'fixed'});
          if ($.isFunction(currentSettings.endResize))
            currentSettings.endResize(modal, currentSettings);
        });
      }
    }
  };

  // Remove the modal function
  $.nyroModalRemove = function() {
    removeModal();
  };

  // Go to the next image for a gallery
  // return false if nothing was done
  $.nyroModalNext = function() {
    var link = getGalleryLink(1);
    if (link)
      return link.nyroModalManual(currentSettings);
    return false;
  };

  // Go to the previous image for a gallery
  // return false if nothing was done
  $.nyroModalPrev = function() {
    var link = getGalleryLink(-1);
    if (link)
      return link.nyroModalManual(currentSettings);
    return false;
  };


  // -------------------------------------------------------
  // Default Settings
  // -------------------------------------------------------

  $.fn.nyroModal.settings = {
    debug: false, // Show the debug in the background

    modal: false, // Esc key or click backgrdound enabling or not

    type: '', // nyroModal type (form, formData, iframe, image, etc...)
    from: '', // Dom object where the call come from
    hash: '', // Eventual hash in the url

    processHandler: null, // Handler just before the real process

    selIndicator: 'nyroModalSel', // Value added when a form or Ajax is sent with a filter content

    formIndicator: 'nyroModal', // Value added when a form is sent

    content: null, // Raw content if type content is used

    bgColor: '#000000', // Background color

    ajax: {}, // Ajax option (url, data, type, success will be overwritten for a form, url and success only for an ajax call)

    swf: { // Swf player options if swf type is used.
      wmode: 'transparent'
    },

    width: null, // default Width If null, will be calculate automatically
    height: null, // default Height If null, will be calculate automatically

    minWidth: 400, // Minimum width
    minHeight: 300, // Minimum height

    resizable: true, // Indicate if the content is resizable. Will be set to false for swf
    autoSizable: true, // Indicate if the content is auto sizable. If not, the min size will be used

    padding: 25, // padding for the max modal size

    regexImg: '[^\.]\.(jpg|jpeg|png|tiff|gif|bmp)\s*$', // Regex to find images
    defaultImgAlt: 'Image', // Default alt attribute for the images
    setWidthImgTitle: true, // Set the width to the image title
    ltr: true, // Left to Right by default. Put to false for Hebrew or Right to Left language

    css: { // Default CSS option for the nyroModal Div. Some will be overwritten or updated when using IE6
      bg: {
        zIndex: 100,
        position: 'fixed',
        top: 0,
        left: 0,
        height: '100%',
        width: '100%'
      },
      wrapper: {
        zIndex: 101,
        position: 'fixed',
        top: '50%',
        left: '50%'
      },
      wrapper2: {
      },
      content: {
        overflow: 'auto'
      },
      loading: {
        zIndex: 102,
        position: 'fixed',
        top: '50%',
        left: '50%',
        marginTop: '-50px',
        marginLeft: '-50px'
      }
    },

    wrap: { // Wrapper div used to style the modal regarding the content type
      div: '<div class="wrapper"></div>',
      ajax: '<div class="wrapper"></div>',
      form: '<div class="wrapper"></div>',
      formData: '<div class="wrapper"></div>',
      image: '<div class="wrapperImg"></div>',
      gallery: '<div class="wrapperImg"><a href="#" class="nyroModalPrev">Prev</a><a href="#"  class="nyroModalNext">Next</a></div>', // Use .nyroModalPrev and .nyroModalNext to set the navigation link
      swf: '<div class="wrapperSwf"></div>',
      iframe: '<div class="wrapperIframe"></div>',
      manual: '<div class="wrapper"></div>'
    },

    closeButton: '<a href="#" class="nyroModalClose" id="closeBut" title="close">Close</a>', // Adding automaticly as the first child of #nyroModalWrapper

    title: null, // Modal title
    titleFromIframe: true, // When using iframe in the same domain, try to get the title from it

    openSelector: '.nyroModal', // selector for open a new modal. will be used to parse automaticly at page loading
    closeSelector: '.nyroModalClose', // selector to close the modal

    contentLoading: '<a href="#" class="nyroModalClose">Cancel</a>', // Loading div content

    errorClass: 'error', // CSS Error class added to the loading div in case of error
    contentError: 'The requested content cannot be loaded.<br />Please try again later.<br /><a href="#" class="nyroModalClose">Close</a>', // Content placed in the loading div in case of error

    handleError: null, // Callback in case of error

    showBackground: showBackground, // Show background animation function
    hideBackground: hideBackground, // Hide background animation function

    endFillContent: null, // Will be called after filling and wraping the content, before parsing closeSelector and openSelector and showing the content
    showContent: showContent, // Show content animation function
    endShowContent: null, // Will be called once the content is shown
    beforeHideContent: null, // Will be called just before the modal closing
    hideContent: hideContent, // Hide content animation function

    showTransition: showTransition, // Show the transition animation (a modal is already shown and a new one is requested)
    hideTransition: hideTransition, // Hide the transition animation to show the content

    showLoading: showLoading, // show loading animation function
    hideLoading: hideLoading, // hide loading animation function

    resize: resize, // Resize animation function
    endResize: null, // Will be called one the content is resized

    updateBgColor: updateBgColor, // Change background color animation function

    endRemove: null // Will be called once the modal is totally gone
  };


  // -------------------------------------------------------
  // Private function
  // -------------------------------------------------------

  // Main function
  function processModal(settings) {
    if (modal.loadingShown || modal.transition || modal.anim)
      return;
    debug('processModal');
    modal.started = true;
    setDefaultCurrentSettings(settings);
    modal.error = false;
    modal.closing = false;
    modal.dataReady = false;
    modal.scripts = new Array();

    currentSettings.type = fileType();

    if ($.isFunction(currentSettings.processHandler))
      currentSettings.processHandler(currentSettings);

    from = currentSettings.from;
    url = currentSettings.url;

    if (currentSettings.type == 'swf') {
      // Swf is transforming as a raw content
      currentSettings.resizable = false;
      setCurrentSettings({overflow: 'hidden'}, 'css', 'content');
      currentSettings.content = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+currentSettings.width+'" height="'+currentSettings.height+'"><param name="movie" value="'+url+'"></param>';
      var tmp = '';
      $.each(currentSettings.swf, function(name, val) {
        currentSettings.content+= '<param name="'+name+'" value="'+val+'"></param>';
        tmp+= ' '+name+'="'+val+'"';
      });
      currentSettings.content+= '<embed src="'+url+'" type="application/x-shockwave-flash" width="'+currentSettings.width+'" height="'+currentSettings.height+'"'+tmp+'></embed></object>';
    }

    if (from) {
      var jFrom = $(from);
      if (currentSettings.type == 'form') {
        var data = $(from).serializeArray();
        data.push({name: currentSettings.formIndicator, value: 1});
        if (currentSettings.selector)
          data.push({name: currentSettings.selIndicator, value: currentSettings.selector.substring(1)});
        $.ajax($.extend({}, currentSettings.ajax, {
            url: url,
            data: data,
            type: from.method,
            success: ajaxLoaded,
            error: loadingError
          }));
        debug('Form Ajax Load: '+jFrom.attr('action'));
        showModal();
      } else if (currentSettings.type == 'formData') {
        // Form with data. We're using a hidden iframe
        initModal();
        jFrom.attr('target', 'nyroModalIframe');
        jFrom.attr('action', url);
        jFrom.prepend('<input type="hidden" name="'+currentSettings.formIndicator+'" value="1" />');
        if (currentSettings.selector)
          jFrom.prepend('<input type="hidden" name="'+currentSettings.selIndicator+'" value="'+currentSettings.selector.substring(1)+'" />');
        modal.tmp.html('<iframe frameborder="0" hspace="0" name="nyroModalIframe"></iframe>');
        $('iframe', modal.tmp)
          .css({
            width: currentSettings.width,
            height: currentSettings.height
          })
          .error(loadingError)
          .load(formDataLoaded);
        debug('Form Data Load: '+jFrom.attr('action'));
        showModal();
        showContentOrLoading();
      } else if (currentSettings.type == 'image' || currentSettings.type == 'gallery') {
        var title = jFrom.attr('title') || currentSettings.defaultImgAlt;
        initModal();
        modal.tmp.html('<img id="nyroModalImg" />').find('img').attr('alt', title);
        debug('Image Load: '+url);
        modal.tmp.css({lineHeight: 0});
        $('img', modal.tmp)
          .error(loadingError)
          .load(function() {
            debug('Image Loaded: '+this.src);
            $(this).unbind('load');
            var w = modal.tmp.width();
            var h = modal.tmp.height();
            modal.tmp.css({lineHeight: ''});
            setCurrentSettings({
              width: w,
              height: h,
              imgWidth: w,
              imgHeight: h
            });
            setCurrentSettings({overflow: 'hidden'}, 'css', 'content');
            modal.dataReady = true;
            if (modal.loadingShown || modal.transition)
              showContentOrLoading();
          })
          .attr('src', url);
        showModal();
      } else if (currentSettings.type == 'iframe') {
        initModal();
        modal.tmp.html('<iframe frameborder="0" hspace="0" src="'+url+'" name="nyroModalIframe"></iframe>');
        debug('Iframe Load: '+url);
        $('iframe', modal.tmp).eq(0)
          .css({
            width: '100%',
            height: isDoctypeStrict()? '99%' : '100%'
          })
          .load(function(e) {
            if (currentSettings.titleFromIframe && url.indexOf(window.location.hostname) > -1)
              $.nyroModalSettings({title: $('iframe', modal.full).contents().find('title').text()});
          });
        currentSettings.autoSizable = false;
        modal.dataReady = true;
        showModal();
      } else if (currentSettings.type) {
        // Could be every other kind of type or a dom selector
        debug('Content: '+currentSettings.type);
        initModal();
        modal.tmp.html(currentSettings.content);
        var w = modal.tmp.width();
        var h = modal.tmp.height();
        var div = $(currentSettings.type);
        if (div.length) {
          setCurrentSettings({type: 'div'});
          w = div.width();
          h = div.height();
          if (contentElt)
            contentEltLast = contentElt;
          contentElt = div;
          modal.tmp.append(div.contents());
        }
        setCurrentSettings({
          width: w,
          height: h
        });
        if (modal.tmp.html())
          modal.dataReady = true;
        else
          loadingError();
        showModal();
        showContentOrLoading();
      } else {
        debug('Ajax Load: '+url);
        setCurrentSettings({type: 'ajax'});
        var data = currentSettings.ajax.data || {};
        if (currentSettings.selector) {
          if (typeof data == "string") {
            data+= '&'+currentSettings.selIndicator+'='+currentSettings.selector.substring(1);
          } else {
            data[currentSettings.selIndicator] = currentSettings.selector.substring(1);
          }
        }
        $.ajax($.extend(true, currentSettings.ajax, {
          url: url,
          success: ajaxLoaded,
          error: loadingError,
          data: data
        }));
        showModal();
      }
    } else if (currentSettings.content) {
      // Raw content not from a DOM element
      debug('Content: '+currentSettings.type);
      setCurrentSettings({type: 'manual'});
      initModal();
      modal.tmp.html($('<div/>').html(currentSettings.content).contents());
      if (modal.tmp.html())
        modal.dataReady = true;
      else
        loadingError();
      showModal();
    } else {
      // What should we show here? nothing happen
    }
  }

  // Update the current settings
  // object settings
  // string deep1 first key where overwrite the settings
  // string deep2 second key where overwrite the settings
  function setDefaultCurrentSettings(settings) {
    debug('setDefaultCurrentSettings');
    currentSettings = $.extend({}, $.fn.nyroModal.settings, settings);
    currentSettings.selector = '';
    currentSettings.borderW = 0;
    currentSettings.borderH = 0;
    currentSettings.resizable = true;
    setMargin();
  }

  function setCurrentSettings(settings, deep1, deep2) {
    if (modal.started) {
      if (deep1 && deep2) {
        $.extend(currentSettings[deep1][deep2], settings);
      } else if (deep1) {
        $.extend(currentSettings[deep1], settings);
      } else {
        $.extend(currentSettings, settings);
      }
    } else {
      if (deep1 && deep2) {
        $.extend($.fn.nyroModal.settings[deep1][deep2], settings);
      } else if (deep1) {
        $.extend($.fn.nyroModal.settings[deep1], settings);
      } else {
        $.extend($.fn.nyroModal.settings, settings);
      }
    }
  }

  // Set the margin for postionning the element. Useful for IE6
  function setMarginScroll() {
    if (isIE6) {
      if (document.documentElement) {
        currentSettings.marginScrollLeft = document.documentElement.scrollLeft;
        currentSettings.marginScrollTop = document.documentElement.scrollTop;
      } else {
        currentSettings.marginScrollLeft = document.body.scrollLeft;
        currentSettings.marginScrollTop = document.body.scrollTop;
      }
    } else {
      currentSettings.marginScrollLeft = 0;
      currentSettings.marginScrollTop = 0;
    }
  }

  // Set the margin for the content
  function setMargin() {
    setMarginScroll();
    currentSettings.marginLeft = -(currentSettings.width+currentSettings.borderW)/2 + currentSettings.marginScrollLeft;
    if(currentSettings.css.wrapper.marginTop) {
      currentSettings.marginTop = currentSettings.css.wrapper.marginTop;
    } else {
      currentSettings.marginTop = -(currentSettings.height+currentSettings.borderH)/2 + currentSettings.marginScrollTop;
    }
  }

  // Set the margin for the current loading
  function setMarginloading() {
    setMarginScroll();
    var outer = getOuter(modal.loading);
    currentSettings.marginTopLoading = -(modal.loading.height() + outer.h.border + outer.h.padding)/2 + currentSettings.marginScrollTop;
    currentSettings.marginLeftLoading = -(modal.loading.width() + outer.w.border + outer.w.padding)/2 + currentSettings.marginScrollLeft;
  }

  // Init the nyroModal div by settings the CSS elements and hide needed elements
  function initModal() {
    debug('initModal');
    if (!modal.full) {
      if (currentSettings.debug)
        setCurrentSettings({color: 'white'}, 'css', 'bg');

      var iframeHideIE = '';
      if (isIE6) {
        body.css({
          height: body.height()+'px',
          width: body.width()+'px',
          position: 'static',
          overflow: 'hidden'
        });
        $('html').css({overflow: 'hidden'});
        setCurrentSettings({
          position: 'absolute',
          height: '110%',
          width: '110%',
          //top: currentSettings.marginScrollTop+'px',
          top: '0px',
          left: currentSettings.marginScrollLeft+'px'
        }, 'css', 'bg');

        setCurrentSettings({position: 'absolute'}, 'css', 'loading');
        setCurrentSettings({position: 'absolute'}, 'css', 'wrapper');

        iframeHideIE = $('<iframe id="nyroModalIframeHideIe"></iframe>')
                .css($.extend({},
                  currentSettings.css.bg, {
                    opacity: 0,
                    zIndex: 50,
                    border: 'none'
                  }));
      }

      body.append($('<div id="nyroModalFull"><div id="nyroModalBg"></div><div id="nyroModalWrapper"><div id="nyroModalContent"></div></div><div id="nyrModalTmp"></div><div id="nyroModalLoading"></div></div>').hide());

      modal.full = $('#nyroModalFull').show();
      modal.bg = $('#nyroModalBg')
        .css($.extend({
            backgroundColor: currentSettings.bgColor
          }, currentSettings.css.bg))
        .before(iframeHideIE);
      if (!currentSettings.modal)
        modal.bg.click(removeModal);
      modal.loading = $('#nyroModalLoading')
        .css(currentSettings.css.loading)
        .hide();
      modal.contentWrapper = $('#nyroModalWrapper')
        .css(currentSettings.css.wrapper)
        .hide();
      modal.content = $('#nyroModalContent');
      modal.tmp = $('#nyrModalTmp').hide();

      // To stop the mousewheel if the the plugin is available
      if ($.isFunction($.fn.mousewheel)) {
        modal.content.mousewheel(function(e, d) {
          var elt = modal.content.get(0);
          if ((d > 0 && elt.scrollTop == 0) ||
              (d < 0 && elt.scrollHeight - elt.scrollTop == elt.clientHeight)) {
            e.preventDefault();
            e.stopPropagation();
          }
        });
      }

      $(document).keydown(keyHandler);
      modal.content.css({width: 'auto', height: 'auto'});
      modal.contentWrapper.css({width: 'auto', height: 'auto'});
    }
  }

  // Show the modal (ie: the background and then the loading if needed or the content directly)
  function showModal() {
    debug('showModal');
    if (!modal.ready) {
      initModal();
      modal.anim = true;
      currentSettings.showBackground(modal, currentSettings, endBackground);
    } else {
      modal.anim = true;
      modal.transition = true;
      currentSettings.showTransition(modal, currentSettings, function(){endHideContent();modal.anim=false;showContentOrLoading();});
    }
  }

  // Used for the escape key or the arrow in the gallery type
  function keyHandler(e) {
    if (e.keyCode == 27) {
      if (!currentSettings.modal)
        removeModal();
    } else if (currentSettings.type == 'gallery' && modal.ready && modal.dataReady && !modal.anim && !modal.transition) {
      if (e.keyCode == 39 || e.keyCode == 40) {
        e.preventDefault();
        $('.nyroModalNext', modal.content).eq(0).trigger('click');
        return false;
      } else if (e.keyCode == 37 || e.keyCode == 38) {
        e.preventDefault();
        $('.nyroModalPrev', modal.content).eq(0).trigger('click');
        return false;
      }
    }
  }

  // Determine the filetype regarding the link DOM element
  function fileType() {
    if (currentSettings.forceType) {
      var tmp = currentSettings.forceType;
      if (!currentSettings.content)
        currentSettings.from = true;
      currentSettings.forceType = null;
      return tmp;
    }

    var from = currentSettings.from;

    var url;

    if (from && from.nodeName) {
      var jFrom = $(from);
      currentSettings.url = url = from.nodeName.toLowerCase() == 'form'? jFrom.attr('action') : from.href;

      if (jFrom.attr('rev') == 'modal')
        currentSettings.modal = true;

      if (jFrom.attr('title'))
        currentSettings.title = jFrom.attr('title');

      var imgType = imageType(url, from);
      if (imgType)
        return imgType;

      if (from.target && from.target.toLowerCase() == '_blank' || (from.hostname && from.hostname.replace(/:\d*$/,'') != window.location.hostname.replace(/:\d*$/,''))) {
        return 'iframe';
      } else if (from.nodeName.toLowerCase() == 'form') {
        setCurrentSettings(extractUrlSel(url));
        if (jFrom.attr('enctype') == 'multipart/form-data')
          return 'formData';
        return 'form';
      }
    } else {
      url = currentSettings.url;
      if (!currentSettings.content)
        currentSettings.from = true;

      if (!url)
        return null;

      var reg1 = new RegExp("^http://", "g");
      if (url.match(reg1))
        return 'iframe';
    }

    var imgType = imageType(url, from);
    if (imgType)
      return imgType;

    var swf = new RegExp('[^\.]\.(swf)\s*$', 'i');
    if (swf.test(url))
      return 'swf';

    var tmp = extractUrlSel(url);
    setCurrentSettings(tmp);

    if (!tmp.url)
      return tmp.selector;
  }

  function imageType(url, from) {
    var image = new RegExp(currentSettings.regexImg, 'i');
    if (image.test(url)) {
      if (from && from.rel)
        return 'gallery';
      else
        return 'image';
    }
  }

  function extractUrlSel(url) {
    var ret = {
      url: null,
      selector: null
    };

    if (url) {
      var hash = getHash(url);
      var hashLoc = getHash(window.location.href);
      var curLoc = window.location.href.substring(0, window.location.href.length - hashLoc.length);
      var req = url.substring(0, url.length - hash.length);

      if (req == curLoc) {
        ret.selector = hash;
      } else {
        ret.url = req;
        ret.selector = hash;
      }
    }
    return ret;
  }

  // Called when the content cannot be loaded or tiemout reached
  function loadingError() {
    debug('loadingError');

    modal.error = true;

    if (!modal.ready)
      return;

    if ($.isFunction(currentSettings.handleError))
      currentSettings.handleError(modal, currentSettings);

    modal.loading
      .addClass(currentSettings.errorClass)
      .html(currentSettings.contentError);
    $(currentSettings.closeSelector, modal.loading).click(removeModal);
    setMarginloading();
    modal.loading
      .css({
        marginTop: currentSettings.marginTopLoading+'px',
        marginLeft: currentSettings.marginLeftLoading+'px'
      });
  }

  // Put the content from modal.tmp to modal.content
  function fillContent() {
    debug('fillContent');
    if (!modal.tmp.html())
      return;

    modal.content.html(modal.tmp.contents());
    modal.tmp.empty();
    wrapContent();

    if ($.isFunction(currentSettings.endFillContent))
      currentSettings.endFillContent(modal, currentSettings);

    modal.content.append(modal.scripts);

    var currentSettingsNew = $.extend({}, currentSettings);
    if (resized.width)
      currentSettingsNew.width = null;
    if (resized.height)
      currentSettingsNew.height = null;
    $(currentSettings.closeSelector, modal.contentWrapper).click(removeModal);
    $(currentSettings.openSelector, modal.contentWrapper).nyroModal(currentSettingsNew);
  }

  // Wrap the content and update the modal size if needed
  function wrapContent() {
    debug('wrapContent');

    var wrap = $(currentSettings.wrap[currentSettings.type]);
    modal.content.append(wrap.children().remove());
    modal.contentWrapper.wrapInner(wrap);

    if (currentSettings.type == 'gallery') {
      // Set the action for the next and prev button (or remove them)

      var linkPrev = getGalleryLink(-1);
      if (linkPrev) {
        $('.nyroModalPrev', modal.contentWrapper)
          .attr('href', linkPrev.attr('href'))
          .click(function(e) {
            e.preventDefault();
            linkPrev.nyroModalManual(currentSettings);
            return false;
          });
      } else {
        $('.nyroModalPrev', modal.contentWrapper).remove();
      }
      var linkNext = getGalleryLink(1);
      if (linkNext) {
        $('.nyroModalNext', modal.contentWrapper)
          .attr('href', linkNext.attr('href'))
          .click(function(e) {
            e.preventDefault();
            linkNext.nyroModalManual(currentSettings);
            return false;
          });
      } else {
        $('.nyroModalNext', modal.contentWrapper).remove();
      }
    }

    calculateSize();
  }

  function getGalleryLink(dir) {
    if (currentSettings.type == 'gallery') {
      if (!currentSettings.ltr)
        dir *= -1;
      // next
      var gallery = $('[rel="'+currentSettings.from.rel+'"]');
      var currentIndex = gallery.index(currentSettings.from);
      var index = currentIndex + dir;
      if (index >= 0 && index < gallery.length)
        return gallery.eq(index);
    }
    return false;
  }

  // Calculate the size for the contentWrapper
  function calculateSize(resizing) {
    debug('calculateSize');

    if (!modal.wrapper)
      modal.wrapper = modal.contentWrapper.children(':first');

    resized.width = false;
    resized.height = false;
    if (currentSettings.autoSizable && (!currentSettings.width || !currentSettings.height)) {
      modal.contentWrapper.css({opacity: 0}).show();
      var tmp = {
        width: 'auto',
        height: 'auto'
      };
      if (currentSettings.width)
        tmp.width = currentSettings.width;
      if (currentSettings.height)
        tmp.height = currentSettings.height;
      modal.content.css(tmp);
      if (!currentSettings.width) {
        currentSettings.width = modal.content.width();
        resized.width = true;
      }
      if (!currentSettings.height) {
        currentSettings.height = modal.content.height();
        resized.height = true;
      }
      modal.contentWrapper.hide().css({opacity: 1});
    }
    currentSettings.width = Math.max(currentSettings.width, currentSettings.minWidth);
    currentSettings.height = Math.max(currentSettings.height, currentSettings.minHeight);

    var outerWrapper = getOuter(modal.contentWrapper);
    var outerWrapper2 = getOuter(modal.wrapper);
    var outerContent = getOuter(modal.content);

    var tmp = {
      content: {
        width: currentSettings.width,
        height: currentSettings.height
      },
      wrapper2: {
        width: currentSettings.width + outerContent.w.total,
        height: currentSettings.height + outerContent.h.total
      },
      wrapper: {
        width: currentSettings.width + outerContent.w.total + outerWrapper2.w.total,
        height: currentSettings.height + outerContent.h.total + outerWrapper2.h.total
      }
    };
    
    if (currentSettings.resizable) {
      var maxHeight = $(window).height()
          - currentSettings.padding*2
          - outerWrapper.h.border
          - (tmp.wrapper.height - currentSettings.height);
      var maxWidth = $(window).width()
          - currentSettings.padding*2
          - outerWrapper.w.border
          - (tmp.wrapper.width - currentSettings.width);

      if (tmp.content.height > maxHeight || tmp.content.width > maxWidth) {
        // We're gonna resize the modal as it will goes outside the view port
        if (currentSettings.type == 'image' || currentSettings.type == 'gallery') {
          // An image is resized proportionnaly
          var diffW = tmp.content.width - currentSettings.imgWidth;
          var diffH = tmp.content.height - currentSettings.imgHeight;
            if (diffH < 0) diffH = 0;
            if (diffW < 0) diffW = 0;
          var calcH = maxHeight - diffH;
          var calcW = maxWidth - diffW;
          var ratio = Math.min(calcH/currentSettings.imgHeight, calcW/currentSettings.imgWidth);

          calcH = Math.floor(currentSettings.imgHeight*ratio);
          calcW = Math.floor(currentSettings.imgWidth*ratio);
          $('img#nyroModalImg', modal.content).css({
            height: calcH+'px',
            width: calcW+'px'
          });
          tmp.content.height = calcH + diffH;
          tmp.content.width = calcW + diffW;
        } else {
          // For an HTML content, we simply decrease the size
          if (currentSettings.minHeight) {
            tmp.content.height = Math.max(Math.min(tmp.content.height, maxHeight), currentSettings.minHeight);
          } else {
            tmp.content.height = Math.min(tmp.content.height, maxHeight);
          }
          if (currentSettings.minWidth) {
            tmp.content.width = Math.max(Math.min(tmp.content.width, maxWidth), currentSettings.minWidth);
          } else {
            tmp.content.width = Math.min(tmp.content.width, maxWidth);
          }
        }
        tmp.wrapper2 = {
            width: tmp.content.width + outerContent.w.total,
            height: tmp.content.height + outerContent.h.total
          };
        tmp.wrapper = {
            width: tmp.content.width + outerContent.w.total + outerWrapper2.w.total,
            height: tmp.content.height + outerContent.h.total + outerWrapper2.h.total
          };
      }
    }
    
    modal.content.css($.extend({}, tmp.content, currentSettings.css.content));
    modal.wrapper.css($.extend({}, tmp.wrapper2, currentSettings.css.wrapper2));

    if (!resizing) {
      modal.contentWrapper.css($.extend({}, tmp.wrapper, currentSettings.css.wrapper));
      if (currentSettings.type == 'image' || currentSettings.type == 'gallery') {
        // Adding the title for the image
        var title = $('img', modal.content).attr('alt');
        $('img', modal.content).removeAttr('alt');
        if (title != currentSettings.defaultImgAlt) {
          var divTitle = $('<div>'+title+'</div>');
          modal.content.append(divTitle);
          if (currentSettings.setWidthImgTitle) {
            var outerDivTitle = getOuter(divTitle);
            divTitle.css({width: (tmp.content.width + outerContent.w.padding - outerDivTitle.w.total)+'px'});
          }
        }
      }

      if (!currentSettings.modal)
        modal.contentWrapper.prepend(currentSettings.closeButton);
    }

    if (currentSettings.title)
      modal.contentWrapper.prepend('<h1 id="nyroModalTitle">'+currentSettings.title+'</h1>');

    tmp.wrapper.borderW = outerWrapper.w.border;
    tmp.wrapper.borderH = outerWrapper.h.border;

    setCurrentSettings(tmp.wrapper);
    setMargin();
  }

  function removeModal(e) {
    debug('removeModal');
    if (e)
      e.preventDefault();
    if (modal.full && modal.ready) {
      modal.ready = false;
      modal.anim = true;
      modal.closing = true;
      if (modal.loadingShown || modal.transition) {
        currentSettings.hideLoading(modal, currentSettings, function() {
            modal.loading.hide();
            modal.loadingShown = false;
            modal.transition = false;
            currentSettings.hideBackground(modal, currentSettings, endRemove);
          });
      } else {
        if (fixFF)
          modal.content.css({position: ''}); // Fix Issue #10, remove the attribute
        modal.wrapper.css({overflow: 'hidden'}); // Used to fix a visual issue when hiding
        modal.content.css({overflow: 'hidden'}); // Used to fix a visual issue when hiding
        if ($.isFunction(currentSettings.beforeHideContent)) {
          currentSettings.beforeHideContent(modal, currentSettings, function() {
            currentSettings.hideContent(modal, currentSettings, function() {
              endHideContent();
              currentSettings.hideBackground(modal, currentSettings, endRemove);
            });
          });
        } else {
          currentSettings.hideContent(modal, currentSettings, function() {
              endHideContent();
              currentSettings.hideBackground(modal, currentSettings, endRemove);
            });
        }
      }
    }
    if (e)
      return false;
  }

  function showContentOrLoading() {
    debug('showContentOrLoading');
    if (modal.ready && !modal.anim) {
      if (modal.dataReady) {
        if (modal.tmp.html()) {
          modal.anim = true;
          if (modal.transition) {
            fillContent();
            currentSettings.hideTransition(modal, currentSettings, function() {
              modal.loading.hide();
              modal.transition = false;
              modal.loadingShown = false;
              endShowContent();
            });
          } else {
            currentSettings.hideLoading(modal, currentSettings, function() {
                modal.loading.hide();
                modal.loadingShown = false;
                fillContent();
                setMarginloading();
                currentSettings.showContent(modal, $.extend({}, currentSettings), endShowContent);
              });
          }
        }
      } else if (!modal.loadingShown && !modal.transition) {
        modal.anim = true;
        modal.loadingShown = true;
        if (modal.error)
          loadingError();
        else
          modal.loading.html(currentSettings.contentLoading);
        $(currentSettings.closeSelector, modal.loading).click(removeModal);
        setMarginloading();
        currentSettings.showLoading(modal, currentSettings, function(){modal.anim=false;showContentOrLoading();});
      }
    }
  }


  // -------------------------------------------------------
  // Private Data Loaded callback
  // -------------------------------------------------------

  function ajaxLoaded(data) {
    debug('AjaxLoaded: '+this.url);
    modal.tmp.html(currentSettings.selector
      ?filterScripts($('<div>'+data+'</div>').find(currentSettings.selector).contents())
      :filterScripts(data));
    if (modal.tmp.html()) {
      modal.dataReady = true;
      showContentOrLoading();
    } else
      loadingError();
  }

  function formDataLoaded() {
    debug('formDataLoaded');
    var jFrom = $(currentSettings.from);
    jFrom.attr('action', jFrom.attr('action')+currentSettings.selector);
    jFrom.attr('target', '');
    $('input[name='+currentSettings.formIndicator+']', currentSettings.from).remove();
    var iframe = modal.tmp.children('iframe');
    var iframeContent = iframe.unbind('load').contents().find(currentSettings.selector || 'body').not('script[src]');
    iframe.attr('src', 'about:blank'); // Used to stop the loading in FF
    modal.tmp.html(iframeContent.html());
    if (modal.tmp.html()) {
      modal.dataReady = true;
      showContentOrLoading();
    } else
      loadingError();
  }


  // -------------------------------------------------------
  // Private Animation callback
  // -------------------------------------------------------

  function endHideContent() {
    debug('endHideContent');
    modal.anim = false;
    if (contentEltLast) {
      contentEltLast.append(modal.content.contents());
      contentEltLast= null;
    } else if (contentElt) {
      contentElt.append(modal.content.contents());
      contentElt= null;
    }
    modal.content.empty();
    modal.contentWrapper
      .empty()
      .removeAttr('style');

    if (modal.closing || modal.transition)
      modal.contentWrapper.hide();

    modal.contentWrapper
      .css(currentSettings.css.wrapper)
      .append(modal.content);
    showContentOrLoading();
  }

  function endRemove() {
    debug('endRemove');
    $(document).unbind('keydown', keyHandler);
    modal.anim = false;
    modal.full.remove();
    modal.full = null;
    if (isIE6) {
      body.css({height: '', width: '', position: '', overflow: ''});
      $('html').css({overflow: ''});
    }
    if ($.isFunction(currentSettings.endRemove))
      currentSettings.endRemove(modal, currentSettings);
  }

  function endBackground() {
    debug('endBackground');
    modal.ready = true;
    modal.anim = false;
    showContentOrLoading();
  }

  function endShowContent() {
    debug('endShowContent');
    modal.anim = false;
    modal.contentWrapper.css({opacity: ''}); // for the close button in IE
    fixFF = $.browser.mozilla && parseFloat($.browser.version) < 1.9 && currentSettings.type != 'gallery' && currentSettings.type != 'image';
    if (fixFF)
      modal.content.css({position: 'fixed'}); // Fix Issue #10
    if ($.isFunction(currentSettings.endShowContent))
      currentSettings.endShowContent(modal, currentSettings);
    if (resized.width)
      setCurrentSettings({width: null});
    if (resized.height)
      setCurrentSettings({height: null});
  }


  // -------------------------------------------------------
  // Utilities
  // -------------------------------------------------------

  // Get the selector from an url (as string)
  function getHash(url) {
    if (typeof url == 'string') {
      var hashPos = url.indexOf('#');
      if (hashPos > -1)
        return url.substring(hashPos);
    }
    return '';
  }

  // Filter an html content to remove the script[src]
  function filterScripts(data) {
    // Removing the body, head and html tag
    if (typeof data == 'string')
      data = data.replace(/<\/?(html|head|body)([^>]*)>/gi, '');
    var tmp = new Array();
    $.each($.clean({0:data}, this.ownerDocument), function() {
      if ($.nodeName(this, "script")) {
        if (!this.src || $(this).attr('rel') == 'forceLoad')
          modal.scripts.push(this);
      } else
        tmp.push(this);
    });
    return tmp;
  }

  // Get the vertical and horizontal margin, padding and border dimension
  function getOuter(elm) {
    elm = elm.get(0);
    var ret = {
      h: {
        margin: getCurCSS(elm, 'marginTop') + getCurCSS(elm, 'marginBottom'),
        border: getCurCSS(elm, 'borderTopWidth') + getCurCSS(elm, 'borderBottomWidth'),
        padding: getCurCSS(elm, 'paddingTop') + getCurCSS(elm, 'paddingBottom')
      },
      w: {
        margin: getCurCSS(elm, 'marginLeft') + getCurCSS(elm, 'marginRight'),
        border: getCurCSS(elm, 'borderLeftWidth') + getCurCSS(elm, 'borderRightWidth'),
        padding: getCurCSS(elm, 'paddingLeft') + getCurCSS(elm, 'paddingRight')
      }
    };

    ret.h.outer = ret.h.margin + ret.h.border;
    ret.w.outer = ret.w.margin + ret.w.border;

    ret.h.inner = ret.h.padding + ret.h.border;
    ret.w.inner = ret.w.padding + ret.w.border;

    ret.h.total = ret.h.outer + ret.h.padding;
    ret.w.total = ret.w.outer + ret.w.padding;

    return ret;
  }

  function getCurCSS(elm, name) {
    var ret = parseInt($.curCSS(elm, name, true));
    if (isNaN(ret))
      ret = 0;
    return ret;
  }

  function isDoctypeStrict() {
    var doctype = '';
    if ($.browser.opera) {
      return true;
    } else if ($.browser.msie) {
      var re = /\s+(X?HTML)\s+([\d\.]+)\s*([^\/]+)*\//gi;
      var res = false;
      if($.browser.msie)
        res = document.all[0].nodeType == 8 ? re.test(document.all[0].nodeValue) : false;
      if (res)
        doctype = RegExp.$3;
    } else
      doctype = document.doctype.systemId;
    return doctype.toLowerCase().indexOf('strict') > -1
  }

  // Proxy Debug function
  function debug(msg) {
    if ($.fn.nyroModal.settings.debug || currentSettings && currentSettings.debug)
      nyroModalDebug(msg, modal, currentSettings || {});
  }

  // -------------------------------------------------------
  // Default animation function
  // -------------------------------------------------------

  function showBackground(elts, settings, callback) {
    elts.bg.css({opacity:0}).fadeTo(500, 0.75, callback);
  }

  function hideBackground(elts, settings, callback) {
    elts.bg.fadeOut(300, callback);
  }

  function showLoading(elts, settings, callback) {
    elts.loading
      .css({
        marginTop: settings.marginTopLoading+'px',
        marginLeft: settings.marginLeftLoading+'px',
        opacity: 0
      })
      .show()
      .animate({
        opacity: 1
      }, {complete: callback, duration: 400});
  }

  function hideLoading(elts, settings, callback) {
    callback();
  }

  function showContent(elts, settings, callback) {
    elts.loading
      .css({
        marginTop: settings.marginTopLoading+'px',
        marginLeft: settings.marginLeftLoading+'px'
      })
      .show()
      .animate({
        width: settings.width+'px',
        height: settings.height+'px',
        marginTop: settings.marginTop+'px',
        marginLeft: settings.marginLeft+'px'
      }, {duration: 350, complete: function() {
        elts.contentWrapper
          .css({
            width: settings.width+'px',
            height: settings.height+'px',
            marginTop: settings.marginTop+'px',
            marginLeft: settings.marginLeft+'px'
          })
          .show();
          elts.loading.fadeOut(200, callback);
        }
      });
  }

  function hideContent(elts, settings, callback) {
    elts.contentWrapper
      .animate({
        height: '50px',
        width: '50px',
        marginTop: (-(25+settings.borderH)/2 + settings.marginScrollTop)+'px',
        marginLeft: (-(25+settings.borderW)/2 + settings.marginScrollLeft)+'px'
      }, {duration: 350, complete: function() {
        elts.contentWrapper.hide();
        callback();
      }});
  }

  function showTransition(elts, settings, callback) {
    // Put the loading with the same dimensions of the current content
    elts.loading
      .css({
        marginTop: elts.contentWrapper.css('marginTop'),
        marginLeft: elts.contentWrapper.css('marginLeft'),
        height: elts.contentWrapper.css('height'),
        width: elts.contentWrapper.css('width'),
        opacity: 0
      })
      .show()
      .fadeTo(400, 1, function() {
          elts.contentWrapper.hide();
          callback();
        });
  }

  function hideTransition(elts, settings, callback) {
    // Place the content wrapper underneath the the loading with the right dimensions
    elts.contentWrapper
      .hide()
      .css({
        width: settings.width+'px',
        marginLeft: settings.marginLeft+'px',
        height: settings.height+'px',
        marginTop: settings.marginTop+'px',
        opacity: 1
      });
    elts.loading
      .animate({
        width: settings.width+'px',
        marginLeft: settings.marginLeft+'px',
        height: settings.height+'px',
        marginTop: settings.marginTop+'px'
      }, {complete: function() {
          elts.contentWrapper.show();
          elts.loading.fadeOut(400, function() {
            elts.loading.hide();
            callback();
          });
        }, duration: 350});
  }

  function resize(elts, settings, callback) {
    elts.contentWrapper
      .animate({
        width: settings.width+'px',
        marginLeft: settings.marginLeft+'px',
        height: settings.height+'px',
        marginTop: settings.marginTop+'px'
      }, {complete: callback, duration: 400});
  }

  function updateBgColor(elts, settings, callback) {
    if (!$.fx.step.backgroundColor) {
      elts.bg.css({backgroundColor: settings.bgColor});
      callback();
    } else
      elts.bg
        .animate({
          backgroundColor: settings.bgColor
        }, {complete: callback, duration: 400});
  }

  // -------------------------------------------------------
  // Default initialization
  // -------------------------------------------------------

  $($.fn.nyroModal.settings.openSelector).nyroModal();

});

// Default debug function, to be overwritten if needed
//      Be aware that the settings parameter could be empty
function nyroModalDebug(msg, elts, settings) {
  console.log(msg);
  if (elts.full)
    elts.bg.prepend(msg+'<br />');
}
