/** * 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: 'kingsparc_upload.aspx', // file upload name name: 'filename', // 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); input.setattribute('id', 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('