/**
  * Ajax upload
  * Project page - http://valums.com/ajax-upload/
  * Copyright (c) 2008 Andris Valums, http://valums.com
  * Licensed under the MIT license (http://valums.com/mit-license/)
  * Version 3.2 (19.05.2009)
  */

/**
  * Changes from the previous version:
  * 1. Input is cleared after submit is canceled to allow user to select same file
  * 2. Fixed problem with FF3 when used on page with smaller font size
  *
  * For the full changelog please visit:
  * http://valums.com/ajax-upload-changelog/
  */

(function(){

var d = document, w = window;

/**
  * Get element by id
  */
function get(element){
  if (typeof element == "string")
    element = d.getElementById(element);
  return element;
}

/**
  * Attaches event to a dom element
  */
function addEvent(el, type, fn){
  if (w.addEventListener){
    el.addEventListener(type, fn, false);
  } else if (w.attachEvent){
    var f = function(){
      fn.call(el, w.event);
    };
    el.attachEvent('on' + type, f)
  }
}


/**
  * Creates and returns element from html chunk
  */
var toElement = function(){
  var div = d.createElement('div');
  return function(html){
    div.innerHTML = html;
    var el = div.childNodes[0];
    div.removeChild(el);
    return el;
  }
}();

function hasClass(ele,cls){
  return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
  if (!hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
  var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
  ele.className=ele.className.replace(reg,' ');
}

// getOffset function copied from jQuery lib (http://jquery.com/)
if (document.documentElement["getBoundingClientRect"]){
  // Get Offset using getBoundingClientRect
  // http://ejohn.org/blog/getboundingclientrect-is-awesome/
  var getOffset = function(el){
    var box = el.getBoundingClientRect(),
    doc = el.ownerDocument,
    body = doc.body,
    docElem = doc.documentElement,

  // for ie
  clientTop = docElem.clientTop || body.clientTop || 0,
  clientLeft = docElem.clientLeft || body.clientLeft || 0,

  // In Internet Explorer 7 getBoundingClientRect property is treated as physical,
  // while others are logical. Make all logical, like in IE8.


  zoom = 1;
  if (body.getBoundingClientRect) {
    var bound = body.getBoundingClientRect();
    zoom = (bound.right - bound.left)/body.clientWidth;
  }
  if (zoom > 1){
    clientTop = 0;
    clientLeft = 0;
  }
  var top = box.top/zoom + (window.pageYOffset || docElem && docElem.scrollTop/zoom || body.scrollTop/zoom) - clientTop,
  left = box.left/zoom + (window.pageXOffset|| docElem && docElem.scrollLeft/zoom || body.scrollLeft/zoom) - clientLeft;

  return {
    top: top,
    left: left
  };
}

} else {
  // Get offset adding all offsets
  var getOffset = function(el){
    if (w.jQuery){
      return jQuery(el).offset();
    }

  var top = 0, left = 0;
  do {
    top += el.offsetTop || 0;
    left += el.offsetLeft || 0;
  }
  while (el = el.offsetParent);

  return {
    left: left,
    top: top
  };
}
}

function getBox(el){
  var left, right, top, bottom;
  var offset = getOffset(el);
  left = offset.left;
  top = offset.top;

  right = left + el.offsetWidth;
  bottom = top + el.offsetHeight;

  return {
    left: left,
    right: right,
    top: top,
    bottom: bottom
  };
}

/**
  * Crossbrowser mouse coordinates
  */
function getMouseCoords(e){
  // pageX/Y is not supported in IE
  // http://www.quirksmode.org/dom/w3c_cssom.html
  if (!e.pageX && e.clientX){
    // In Internet Explorer 7 some properties (mouse coordinates) are treated as physical,
    // while others are logical (offset).
    var zoom = 1;
    var body = document.body;

  if (body.getBoundingClientRect) {
    var bound = body.getBoundingClientRect();
    zoom = (bound.right - bound.left)/body.clientWidth;
  }

  return {
    x: e.clientX / zoom + d.body.scrollLeft + d.documentElement.scrollLeft,
    y: e.clientY / zoom + d.body.scrollTop + d.documentElement.scrollTop
  };
}

  return {
    x: e.pageX,
    y: e.pageY
  };

}
/**
  * Function generates unique id
  */
var getUID = function(){
  var id = 0;
  return function(){
    return 'ValumsAjaxUpload' + id++;
  }
}();

function fileFromPath(file){
  return file.replace(/.*(\/|\\)/, "");
}

function getExt(file){
  return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
}

// Please use AjaxUpload , Ajax_upload will be removed in the next version
Ajax_upload = AjaxUpload = function(button, options){
  if (button.jquery){
    // jquery object was passed
    button = button[0];
  } else if (typeof button == "string" && /^#.*/.test(button)){
    button = button.slice(1);
  }
  button = get(button);

  this._input = null;
  this._button = button;
  this._disabled = false;
  this._submitting = false;
  // Variable changes to true if the button was clicked
  // 3 seconds ago (requred to fix Safari on Mac error)
  this._justClicked = false;
  this._parentDialog = d.body;

  if (window.jQuery && jQuery.ui && jQuery.ui.dialog){
    var parentDialog = jQuery(self._button).parents('.ui-dialog-content');
    if (parentDialog.length){
      this._parentDialog = parentDialog[0];
    }
  }

  this._settings = {
    // Location of the server-side upload script
    action: 'upload.php',
    // File upload name
    name: 'userfile',
    // Additional data to send
    data: {},
    // Submit file as soon as it's selected
    autoSubmit: true,
    // The type of data that you're expecting back from the server.
    // Html and xml are detected automatically.
    // Only useful when you are using json data as a response.
    // Set to "json" in that case.
    responseType: false,
    // When user selects a file, useful with autoSubmit disabled
    onChange: function(file, extension){},
    // Callback to fire before file is uploaded
    // You can return false to cancel upload
    onSubmit: function(file, extension){},
    // Fired when file upload is completed
    // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
    onComplete: function(file, response) {}
  };

  // Merge the users options with our defaults
  for (var i in options) {
    this._settings[i] = options[i];
  }

  this._createInput();
  this._rerouteClicks();
}

// assigning methods to our class
AjaxUpload.prototype = {
  setData : function(data){
    this._settings.data = data;
  },
  disable : function(){
    this._disabled = true;
  },
  enable : function(){
    this._disabled = false;
  },
  // removes ajaxupload
  destroy : function(){
    if(this._input){
      if(this._input.parentNode){
        this._input.parentNode.removeChild(this._input);
      }
      this._input = null;
    }
  },
  /**
    * Creates invisible file input above the button
    */
  _createInput : function(){
    var self = this;
    var input = d.createElement("input");
    input.setAttribute('type', 'file');
    input.setAttribute('name', this._settings.name);
    var styles = {
      'position' : 'absolute'
      ,'margin': '-5px 0 0 -175px'
      ,'padding': 0
      ,'width': '220px'
      ,'height': '30px'
      ,'fontSize': '14px'
      ,'opacity': 0
      ,'cursor': 'pointer'
      ,'display' : 'none'
      ,'zIndex' :  2147483583 //Max zIndex supported by Opera 9.0-9.2x
      // Strange, I expected 2147483647
    };
    for (var i in styles){
      input.style[i] = styles[i];
    }

  // Make sure that element opacity exists
  // (IE uses filter instead)
  if ( ! (input.style.opacity === "0")){
    input.style.filter = "alpha(opacity=0)";
  }

  this._parentDialog.appendChild(input);

  addEvent(input, 'change', function(){
    // get filename from input
    var file = fileFromPath(this.value);
    if(self._settings.onChange.call(self, file, getExt(file)) == false ){
      return;
    }
    // Submit form when value is changed
    if (self._settings.autoSubmit){
      self.submit();
    }
  });

  // Fixing problem with Safari
  // The problem is that if you leave input before the file select dialog opens
  // it does not upload the file.
  // As dialog opens slowly (it is a sheet dialog which takes some time to open)
  // there is some time while you can leave the button.
  // So we should not change display to none immediately
  addEvent(input, 'click', function(){
    self.justClicked = true;
    setTimeout(function(){
      // we will wait 3 seconds for dialog to open
      self.justClicked = false;
    }, 3000);
  });

  this._input = input;
},
_rerouteClicks : function (){
  var self = this;

  // IE displays 'access denied' error when using this method
  // other browsers just ignore click()
  // addEvent(this._button, 'click', function(e){
  //   self._input.click();
  // });

  var box, dialogOffset = {top:0, left:0}, over = false;
  addEvent(self._button, 'mouseover', function(e){
    if (!self._input || over) return;
    over = true;
    box = getBox(self._button);

  if (self._parentDialog != d.body){
    dialogOffset = getOffset(self._parentDialog);
  }
});


  // we can't use mouseout on the button,
  // because invisible input is over it
  addEvent(document, 'mousemove', function(e){
    var input = self._input;
    if (!input || !over) return;

  if (self._disabled){
    removeClass(self._button, 'hover');
    input.style.display = 'none';
    return;
  }

  var c = getMouseCoords(e);

  if ((c.x >= box.left) && (c.x <= box.right) &&
  (c.y >= box.top) && (c.y <= box.bottom)){
    input.style.top = c.y - dialogOffset.top + 'px';
    input.style.left = c.x - dialogOffset.left + 'px';
    input.style.display = 'block';
    addClass(self._button, 'hover');
  } else {
    // mouse left the button
    over = false;
    if (!self.justClicked){
      input.style.display = 'none';
    }
    removeClass(self._button, 'hover');
  }
});

  },
  /**
    * Creates iframe with unique name
    */
  _createIframe : function(){
    // unique name
    // We cannot use getTime, because it sometimes return
    // same value in safari :(
    var id = getUID();

  // Remove ie6 "This page contains both secure and nonsecure items" prompt
  // http://tinyurl.com/77w9wh
  var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
  iframe.id = id;
  iframe.style.display = 'none';
  d.body.appendChild(iframe);
  return iframe;
},
/**
  * Upload file without refreshing the page
  */
submit : function(){
  var self = this, settings = this._settings;

  if (this._input.value === ''){
    // there is no file
    return;
  }

  // get filename from input
  var file = fileFromPath(this._input.value);

  // execute user event
  if (! (settings.onSubmit.call(this, file, getExt(file)) == false)) {
    // Create new iframe for this submission
    var iframe = this._createIframe();

  // Do not submit if user function returns false
  var form = this._createForm(iframe);
  form.appendChild(this._input);

  form.submit();

  d.body.removeChild(form);
  form = null;
  this._input = null;

  // create new input
  this._createInput();

  var toDeleteFlag = false;

  addEvent(iframe, 'load', function(e){
    if (iframe.src == "about:blank"){
      // First time around, do not delete.
      if( toDeleteFlag ){
        // Fix busy state in FF3
        setTimeout( function() {
          d.body.removeChild(iframe);
        }, 0);
      }
      return;
    }

  var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document;

  // fixing Opera 9.26
  if (doc.readyState && doc.readyState != 'complete'){
    // Opera fires load event multiple times
    // Even when the DOM is not ready yet
    // this fix should not affect other browsers
    return;
  }

  // fixing Opera 9.64
  if (doc.body && doc.body.innerHTML == "false"){
    // In Opera 9.64 event was fired second time
    // when body.innerHTML changed from false
    // to server response approx. after 1 sec
    return;
  }

  var response;

  if (doc.XMLDocument){
    // response is a xml document IE property
    response = doc.XMLDocument;
  } else if (doc.body){
    // response is html document or plain text
    response = doc.body.innerHTML;
    if (settings.responseType == 'json'){
      response = window["eval"]("(" + response + ")");
    }
  } else {
    // response is a xml document
    var response = doc;
  }

  settings.onComplete.call(self, file, response);

  // Reload blank page, so that reloading main page
  // does not re-submit the post. Also, remember to
  // delete the frame
  toDeleteFlag = true;
  iframe.src = "about:blank"; //load event fired
});

  } else {
    // clear input to allow user to select same file
    // Doesn't work in IE6
    // this._input.value = '';
    d.body.removeChild(this._input);
    this._input = null;

  // create new input
  this._createInput();
}
},
/**
  * Creates form, that will be submitted to iframe
  */
_createForm : function(iframe){
  var settings = this._settings;

  // method, enctype must be specified here
  // because changing this attr on the fly is not allowed in IE 6/7
  var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
  form.style.display = 'none';
  form.action = settings.action;
  form.target = iframe.name;
  d.body.appendChild(form);

  // Create hidden input element for each data key
  for (var prop in settings.data){
    var el = d.createElement("input");
    el.type = 'hidden';
    el.name = prop;
    el.value = settings.data[prop];
    form.appendChild(el);
  }
  return form;
}
};
})();
