/**
 * 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.0 (05.04.2009)
 */

/**
 * Changes from the previous version
 * 1. Fixed Opera 9.64 response problem
 * 2. Added responseType parameter
 *
 * 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;

        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
                onComplete: function(file, response) {},
                // Added by Matley
                upload_progress_bar: false,
                upload_progress_url: false
        };

        // 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;
        },
        // use setData instead, set_data will be removed in the next version
        set_data : function(data){
                this.setData(data);
        },
        // 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': '10px'
                        ,'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)";
                }
                d.body.appendChild(input);

                if (window.jQuery && jQuery.ui && jQuery.ui.dialog){
                        // make plugin compatible when using with ui-dialogs with modal = true
                        jQuery(input)
                                .wrap('<div></div>')
                                .parent()
                                .addClass('ui-dialog-ajaxupload')
                                .css({
                                        'zIndex': styles.zIndex
                                        ,'width': 0
                                        ,'height': 0
                                        ,'padding': 0
                                        ,'margin': 0
                                });
                }

                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();
                        }
                });

                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, over = false;
                addEvent(self._button, 'mouseover', function(e){
                        if (!self._input || over) return;
                        over = true;
                        box = getBox(self._button);

                });

                // 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 + 'px';
                                input.style.left = c.x + 'px';
                                input.style.display = 'block';
                                addClass(self._button, 'hover');
                        } else {
                                // mouse left the button
                                over = false;
                                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);

                        jQuery(form).trigger("submit");

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

                        // create new input
                        this._createInput();

                        var toDeleteFlag = false;
                        var fired = 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;
                                }

                                if (fired){
                                        // Event was already fired
                                        // fixing Opera 9.64 that do it multiple times
                                        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
                                fired = true;

                                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
                                        window.setTimeout(function(){
                                                onLoad();
                                        }, 4321);
                                } else {
                                        onLoad();
                                }

                                function onLoad(){
                                        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
                        this._input.value = '';
                }
        },
        /**
         * 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>');

                // Modified by Matley. Add upload progress
                if (settings.upload_progress_url) {
                    form = toElement('<form id="form_' + settings.upload_progress_bar  + '" method="post" enctype="multipart/form-data"></form>');
                    jQuery(form).uploadProgress({
                                progressBar: settings.upload_progress_bar,
                                progressUrl: settings.upload_progress_url
                                });
                };

                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;
        }
};
})();
