/*
 * SPWS MagicAjax
 * Version -1 (0000-00-00)
 * Library to make a website full Ajax without any JS embedded in HTML.
 * Requires: jQuery 1.4.2
 * 
 * Written by Maarten ter Horst,
 * Copyright (c) Maarten ter Horst 2010.
 * 
 * Science Park Web Solutions
 * http://www.scienceparkwebsolutions.nl/
 */

var ma = {

  root: "/",
  title: "Guitar Community",
  debug: true,  // display error 500 details
  request_url: '',
  mutationResponders: [],
  statuses: "error loading ready",
  preload: ['media/img/loading.gif',
            'media/img/warning.png',
            'media/img/info.png'],
  check_hash_interval_time: 250,
  loading_display_timeout_time: 0,
  navigate_effect_time: 200,
  
  displayLoadingStatus: function(element, status, response, silent) {
    var errormessage =
      '<div class="message overlay">' +
        '<p> This page cannot be loaded.' +
          ((typeof response !== 'undefined' && response.status != 200) ?
          (' The following error occurred: ' + response.status + ' ' +
          response.statusText) + '.' : '') +
        '</p>' +
      '</div>';
    
    // display internal server error report in new window
    if (status == 'error' && response.status == 500 && ma.debug == true && !silent) {
      var win = window.open(); // a window object
      win.document.open("text/html", "replace");
      win.document.write(response.responseText);
      win.document.close();
    }
    
    if (element == null) {
      if (status == 'error' && !silent) {
        var redirect = confirm('The page cannot be loaded. Redirect to home page?');
        if (redirect)
          location.replace(ma.root);
      }
      return;
    }
    if ($(element).find('.ajaxStatus').length == 0)
      $(element).html($(element).html().replace(/^\s+/, ''))
          .append('<div class="ajaxStatusContainer">' +
          '<div class="absolute ajaxStatus"></div></div>');
    $(element).find('.ajaxStatus').removeClass(ma.statuses)
        .addClass(status).find('.message').remove();
    
    if (status == 'error') {
      // TODO: onderbrengen in template en betere ui maken.
      $(errormessage).appendTo($(element).find('.ajaxStatus'));
    }
  },
  
  displayContent: function(container, update) {
    // TODO: overgangseffecten ondersteunen. Standaardovergangseffect:
    // container div resizen met animatie en tegelijkertijd de oude content
    // uit laten vloeien en de nieuwe in laten vloeien. Zoals in system preferences
    // op de mac.
    // Daarnaast: custom overgangseffecten mogelijk maken voor containers
    // die aan een bepaalde css expressie voldoen.
    var content = $('#just_loaded').detach().children();
    if (update == 'replace')
      $(container).html('');
    if (update == 'prepend')
      $(content).prependTo(container);
    else
      $(content).appendTo(container);
    if (update != 'replace')
      $(content).addClass('ajax-loaded').removeClass('ajax-loaded', 300);
  },
  
  selectCurrent: function() {
    // TODO: goed met de url query (?...) overweg kunnen.
    $('a[href]').removeClass('selected').each(function() {
      if (ma.location.match('^' + $(this).attr('href')))
        $(this).addClass('selected');
    });
  },
  
  onAjaxError: function(source, response, silent) {
    $('#just_loaded').remove();
    return ma.displayLoadingStatus(source, 'error', response, silent);
  },
  
  onload: function(source, data, status, response, silent) {
    if ((status != "success" && status != "notmodified") || data == "")
      return ma.onAjaxError(source, response, silent);
    
    var title = $('#just_loaded').find('.data').attr('data-title');
    var target = $('#just_loaded').find('.data').attr('data-target');
    var type = $('#just_loaded').find('.data').attr('data-type');
    var clear = $('#just_loaded').find('.data').attr('data-clear');
    var update = $('#just_loaded').find('.data').attr('data-update');
    var subtarget = $('#loaded_before').data('target');
    
    if (typeof clear !== 'undefined' && clear != '')
      $(clear).html('');
    if (type == 'script') {
      $('#dv_scripts').html('');
      $('#just_loaded').detach().children().appendTo('#dv_scripts');
      return ma.displayLoadingStatus(source, 'ready');
    }
    if (typeof target === 'undefined')
      return ma.onAjaxError(source, response, silent);
    if (typeof update === 'undefined')
      update = 'replace';
    
    if ($('#loaded_before').length == 0)
      document.title = ma.title + (title && title.length > 0 ? ': ' + title : '');
    else
      $('#loaded_before').detach().children().appendTo($(subtarget).html(''));
    
    if ($(target).length == 0) {
      if (ma.request_url == ma.root)
        return ma.onAjaxError(source, response, silent);
      else {
        $('#just_loaded').attr('id', 'loaded_before').data('target', target);
        // regex: delete deepest directory in url before query
        var to_load = ma.request_url.replace(/[^\/\?]+(\/(\?.*)?)$/, '$2');
        // TODO: dit kan verwijderd als de django component van MagicAjax er is
        if (to_load == ma.root)
          return ma.onAjaxError(source, response, silent);
        ma.load(to_load, '', source);
      }
    }
    else {
      ma.displayContent(target, update);
      ma.mutation();
      ma.displayLoadingStatus(source, 'ready');
    }
  },
  
  load: function(url, data, element, silent) {
    // TODO: als de url een parent is van huidige url, load niets maar delete
    //       betreffende subpagina uit dom.
    // TODO: de query die reeds in de url staat meenemen in de request_url.
    //       algoritme om 2 hashes te mergen. Bijv:
    //       url ?a=1&b=2&c=3 en link ?b=5&d=8 wordt ?a=1&b=5&c=3&d=8
    if (typeof silent === 'undefined')
      silent = false;
    ma.request_url = url;
    $('<div id="just_loaded"></div>').hide().appendTo('body')
        .load(url, data, function(data, status, response) {
      ma.onload(element, data, status, response, silent);
    });
    ma.displayLoadingStatus(element, 'loading');
  },
  
  onclick: function(event) {
    if ($(this).hasClass('submit')) {
      $(this).closest('form').submit();
      return false;
    }
    if ($(this).is(':not([href])') || $(this).hasClass('sync')
      || event.ctrlKey || event.metaKey)
      return true;
    if (/*$(this).is(':not([href^="' + ma.root + '"])') ||*/
        $(this).hasClass('external')) {
      window.open($(this).attr('href'));
      return false;
    }
    
    var href = $(this).attr('href');
    if (href.match('http')) {
      var urlParts = /^(https?:\/\/.+?)?(\/.+?)(\?.*?)?$/.exec($(this).attr('href'));
      href = urlParts[2] + urlParts[3];
    }
    if (!$(this).hasClass('nonav'))
      ma.pushHistoryState(href);
    
    ma.load(href, '', this);
    return false;
  },
  
  onsubmit: function(event) {
    if ($(this).hasClass('sync') || $(this).is(':not([action])') ||
        !$(this).attr('action').match("^" + ma.root) == ma.root)
      return true;
    if ($(this).attr('enctype') == 'multipart/form-data') {
      return ma.uploadFile(this);
    }
    
    var action = $(this).attr('action');
    var data = $(this).is('[method="post"]') ?
        $(this).serializeArray() : $(this).serialize();
    if (!$(this).hasClass('nonav'))
      ma.pushHistoryState(action + (typeof data === 'string' ? '?' + data : ''));
    
    ma.load(action, data, this);
    return false;
  },
  
  ajaxUploadWithProgressSupported: function() {
    var fi = document.createElement('input');
    fi.type = 'file';
    var xhr = new XMLHttpRequest();
    return 'files' in fi && xhr && ('upload' in xhr) && ('onprogress' in xhr.upload) && window.FormData;
  },
  
  onUploadProgress: function(event, container) {
    var fraction = (1.0 * event.loaded / event.total);
    $(container).find('.progressBarIndicator').width(
        parseInt(fraction * $(container).find('.progressBar').width()));
    $(container).find('.progressText').html(parseInt(fraction * 100));
  },
  
  uploadFileViaAjax: function(form) {
    var file = $(form).find('input[type=file]')[0].files[0];
    var container = $(form).parent();
    var xhr = new XMLHttpRequest();
    xhr.upload.addEventListener('progress', function(event) {
      ma.onUploadProgress(event, container);
    }, false);
    xhr.addEventListener('load', function(event) {
      $(container).html(xhr.responseText);
    }, false);
    
    var formData = new FormData(); 
    formData.append("csrfmiddlewaretoken",
        $(form).find('input[name=csrfmiddlewaretoken]').val());  
    formData.append("file", file);
    xhr.open($(form).attr('method'), $(form).attr('action'), true);
    xhr.send(formData);
    
    $(container).html(
      '<p>' +
        '<span class="grey">Uploading file:</span> ' + file.fileName +
        '<br />' +
        /*'<a class="textonly button">Cancel</a> &nbsp; ' +*/
        '<span class="progressBarWrapper"><span class="progressBar"><span class="progressBarIndicator"></span></span></span> ' +
        '(<span class="progressText">0</span>%)' +
      '</p>'
    );
    
    return false;
  },
  
  uploadFileViaIFrame: function(form) {
    var container = $(form).parent();
    
    $('<iframe id="upload_target" name="upload_target" ' +
        'class="invisible"></iframe>').insertAfter(container);
    $(form).attr('target', 'upload_target');
    $('#upload_target').load(function(event) {
      //$(container).html($(this)[0].contentWindow.document.body.innerHTML);
      $(container).html($(this).contents().find('body').html());
      $(this).remove();
    });
    $(form).addClass('invisible');
    $(container).append(
      '<p>' +
        '<span class="grey">Uploading file:</span> ' +
        $(form).find('input[type=file]').val().match(/[^\/\\]+$/) + ' ' +
        '<img src="/media/img/loading.gif" alt="..." />' +
      '</p>'
    );
    
    return true;
  },
  
  uploadFile: function(form) {
    if (ma.ajaxUploadWithProgressSupported()) {
      return ma.uploadFileViaAjax(form);
    } else {
      return ma.uploadFileViaIFrame(form);
    }
  },
  
  pushHistoryState: function(url) {
    ma.location = url;
    ma.prevLocation = url;
    window.location.hash =
        (url != ma.root) ? '#' + url.replace(ma.root, '') : '';
  },
  
  pathChangeHandler: function() {
    // TODO: vervanging van hash truc voor browsers met history management
  },
  
  checkHash: function() {
    ma.prevLocation = ma.root + window.location.hash.substring(1);
    ma.location = ma.prevLocation;
    if (ma.location.length > ma.root.length)
      ma.load(ma.location, '', null);
  },
  
  recheckHash: function() {
    ma.location = ma.root + window.location.hash.substring(1);
    if (ma.location != ma.prevLocation) {
      ma.load(ma.location, '', null);
      ma.prevLocation = ma.location;
    }
  },
  
  initiateHistory: function() {
    ma.checkHash();
    ma.checkHashInterval = setInterval(
      ma.recheckHash, ma.check_hash_interval_time);
  },
  
  respond: function(array, func) {
    if (typeof func === 'function') {
      array.push(func);
      func();
    }
    else
      for (var i in array)
        array[i]();
  },
  
  mutation: function(func) {
    ma.respond(ma.mutationResponders, func);
  }

};

/*
 * Laadt plaatjes van tevoren in om ze te kunnen gebruiken als bg
 * http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript
 */
(function($) {
  var cache = [];
  
  $.preloadImages = function(images) {
    for (var i in images) {
      var cacheImage = document.createElement('img');
      cacheImage.src = ma.root + images[i];
      cache.push(cacheImage);
    }
  }
})(jQuery)

$(function() {
  ma.initiateHistory();
  ma.mutation(ma.selectCurrent);
  
  $('a').live('click', ma.onclick);
  $('form').live('submit', ma.onsubmit);
  if ($.browser.msie)
    $('input.autosubmit').live('click', function() {
      $(this).focus(); $(this).blur(); $(this).focus();
    });
  $('input.autosubmit').live('change', function() { $(this).closest('form').submit() });
  $('.returntosubmit').live('keydown', function(event) {
    if (event.keyCode == 13) {
      if ($(this).val() != '')
        $(this).closest('form').submit();
      return false;
    }
  });
  
  $.preloadImages(ma.preload);
});
