diff --git a/common/js/plugins/uploader/plugin.load b/common/js/plugins/uploader/plugin.load new file mode 100644 index 000000000..c24671bf6 --- /dev/null +++ b/common/js/plugins/uploader/plugin.load @@ -0,0 +1 @@ +uploader.js diff --git a/common/js/plugins/uploader/uploader.js b/common/js/plugins/uploader/uploader.js new file mode 100644 index 000000000..2eac29527 --- /dev/null +++ b/common/js/plugins/uploader/uploader.js @@ -0,0 +1,773 @@ +/** + * Uploader + */ +(function($){ + +var runtime; + +var Uploader = xe.createApp('Uploader', { + settings : {}, + files : [], + init : function(browseButton, opts) { + if (!runtime) { + $.each(runtimes, function(key,obj) { + if (obj.check()) { runtime = key; return false; } + }); + } + + this.queue = []; + this.settings = $.extend({ + filters : {}, + params : {} + }, opts); + this.settings.browse = $(browseButton); + + if (!this.settings.browse.length) throw 'The parameter browseButton is not valid object or selector.'; + + var s = this.settings; + + if (s.dropzone) s.dropzone = $(s.dropzone); + if (s.upload) s.upload = $(s.upload); + + runtimes[runtime].create(this, this.settings); + }, + API_ON_START : function(sender, params) { + if ($.isFunction(this.settings.onstart)) { + this.settings.onstart.apply(this, params); + } + }, + API_ON_FINISH : function(sender, params) { + if ($.isFunction(this.settings.onfinish)) { + this.settings.onfinish.apply(this, params); + } + }, + API_ON_SELECT : function(sender, params) { + if ($.isFunction(this.settings.onselect)) { + this.settings.onselect.apply(this, params); + } + }, + API_ON_STARTONE : function(sender, params) { + if ($.isFunction(this.settings.onstartone)) { + this.settings.onstartone.apply(this, params); + } + }, + API_ON_FINISHONE : function(sender, params) { + if ($.isFunction(this.settings.onfinishone)) { + this.settings.onfinishone.apply(this, params); + } + }, + API_ON_PROGRESS : function(sender, params) { + if ($.isFunction(this.settings.onprogress)) { + this.settings.onprogress.apply(this, params); + } + }, + API_START : function(sender, params) { + var files = $.grep(this.files, function(file){ return (file.status != 'DONE') }); + + runtimes[runtime].upload(this, this.settings, files); + }, + API_STOP : function(sender, params) { + runtimes[runtime].stop(this, this.settings); + }, + API_SET_PARAM : function(sender, params) { + this.settings.params[params[0]] = params[1]; + }, + API_DEL_PARAM : function(sender, params) { + try { + delete this.settings.params[params[0]]; + } catch(e){} + } +}); + +// Shortcut function in jQuery +$.fn.uploader = function(opts) { + var u = new Uploader(this.eq(0), opts); + if (u) xe.registerApp(u); + + return u; +}; + +// Shortcut function in XE +xe.createUploader = function(browseButton, opts) { + var u = new Uploader(browseButton, opts); + if (u) xe.registerApp(u); + + return u; +}; + +// {{{ runtimes +var runtimes = {}; + +// Google Gears +runtimes.gears = { + _desktop : null, + create : function(uploader, settings) { + var self = this; + var opt = {filter:[]}; + + if (!window.google || google.gears || !google.gears.factory) this.createFactory(); + if (!this._desktop && google.gears.factory) this._desktop = google.gears.factory.create('beta.desktop'); + if (!this._desktop) return false; + + // browse button + settings.browse.click(function(){ + self._desktop.openFiles(onselect, opt); + return false; + }); + + // file filters + $.each(settings.filters, function(k,v){ opt.filter=$.merge(opt.filter,v.split(' ')) }); + opt.filter = $.map(opt.filter, function(ext){ return '.'+ext }); + + // select file callback + function onselect(files) { + var old_length = uploader.files.length; + + $.each(files, function() { + uploader.files.push(new File(this, this.blob.length)) + }); + + uploader.cast('ON_SELECT', [uploader.files, old_length]); + } + + // drag and drop + if (settings.dropzone) { + settings.dropzone + .bind('dragover', function(){ return false; }) + .bind('drop', function(event) { + var data = self._desktop.getDragData(event.originalEvent, 'application/x-gears-files'); + var files = $.grep(data.files, function(file) { + var ext = file.name.match(/\.[a-z0-9]+$/)[0] || ''; + return ($.inArray(ext, opt.filter)!=-1); + }); + + onselect(files); + + return false; + }); + } + + // upload + if (settings.upload) { + settings.upload.click(function(){ + uploader.cast('START'); + return false; + }); + } + }, + createFactory : function() { + var f, m; + + if (!window.google) window.google = {}; + if (!google.gears) { + google.gears = {}; + + if (typeof(GearsFactory)!='undefined'){f = new GearsFactory()} // Firefox + else { + try{ // IE + f = new ActiveXObject('Gears.Factory'); + }catch(e){ // Safari + if ((typeof(m=navigator.mimeTypes)!='undefined')&&(m['application/x-googlegears'])) { + f = $('').appendTo(document.documentElement); + } + } + } + if(f) google.gears.factory = f; + } + }, + check : function() { + if ((window.google && google.gears && google.gears.factory) || (typeof(GearsFactory) != 'undefined')) return true; + + try { + this.factory = new ActiveXObject('Gears.factory'); + return true; + } catch(e) { + var m = navigator.mimeTypes; + if (m && m['application/x-googlegears']) return true; + } + }, + upload : function(uploader, settings, files) { + if (uploader.request || !files.length) return false; + + uploader.cast('ON_START'); + + (function uploadNext() { + var file = files.shift(); + var req = google.gears.factory.create('beta.httprequest'); + var blob = google.gears.factory.create('beta.blobbuilder'); + var bndr = '--------------xe-boundary'+random(); + var data, gap; + + $.each(settings.params, function(key, val) { + blob.append( + '--'+bndr+'\r\n'+ + 'Content-Disposition: form-data; name="'+key+'"\r\n\r\n'+ + val+'\r\n' + ); + }); + + blob.append( + '--'+bndr+'\r\n'+ + 'Content-Disposition: form-data; name="Filedata"; filename="'+file.name+'"\r\n'+ + 'Content-Type: application/octet-stream\r\n\r\n' + ); + + blob.append(file.object.blob); + blob.append('\r\n--'+bndr+'--\r\n'); + + data = blob.getAsBlob(); + gap = data.length - file.size; + + uploader.cast('ON_STARTONE', [file]); + + req.open('POST', settings.url); + req.setRequestHeader('Content-Type', 'multipart/form-data; boundary='+bndr); + req.onreadystatechange = function() { + if (req.readyState == 1) { + file.status = 'UPLOADING'; + return; + } + if (req.readyState != 4) return; + + uploader.request = null; + + if (req.status == 200) { + file.status = 'DONE'; + } else { + file.status = 'FAILED'; + } + + if (files.length) { + uploadNext(); + } else { + setTimeout(function(){ uploader.cast('ON_FINISH') }, 0); + } + + uploader.cast('ON_FINISHONE', [file]); + }; + req.upload.onprogress = function(event) { + if (event.lengthComputable) { + file.loaded = Math.max(event.loaded - gap, 0); + uploader.cast('ON_PROGRESS', [file]); + } + }; + req.send(data); + })(); + }, + stop : function(uploader, settings) { + if (uploader.request) { + uploader.request.abort(); + uploader.request = null; + } + + uploader.cast('ON_STOP'); + } +}; + +// HTML5 +runtimes.html5 = { + create : function(uploader, settings) { + var self = this; + var filter = []; + + // filter by extension + $.each(settings.filters, function(k,v){ filter=$.merge(filter,v.split(' ')) }); + + // when file is selected + function onselect() { + + var files = $.grep(this.files, function(file) { + var ext = (file.fileName.match(/\.([^\.]+)$/)[1] || '').toLowerCase(); + return ($.inArray(ext,filter) != -1); + }); + + if (files.length) { + var old_length = uploader.files.length; + + $.each(files, function(idx, file){ + uploader.files.push(new File(file, file.size||file.fileSize)); + }); + uploader.cast('ON_SELECT', [uploader.files, old_length]); + } + + this.value = ''; + } + + function make_button(event) { + var op = this.offsetParent; + var ow = this.offsetWidth; + var oh = this.offsetHeight; + var ot = this.offsetTop; + var ol = this.offsetLeft; + + if (!uploader.browseButton) { + uploader.browseButton = $('
').appendTo(op); + uploader.browseButton.find('>input').css({cursor:'pointer',opacity:0}).change(onselect); + } + + uploader.browseButton.css({width:ow+'px', height:oh+'px', left:ol+'px', top:ot+'px', margin:0, padding:0}); + + if (event.type == 'focus') uploader.browseButton.find('>input').focus(); + } + + // browse button + settings.browse.bind({mouseover:make_button,focus:make_button}); + + // drag and drop (available only Firefox) + if (settings.dropzone) { + settings.dropzone + .bind('dragover', function(){ return false; }) + .bind('drop', function(event){ + var data = event.originalEvent.dataTransfer; + var obj = {files:data.files||[]}; + + onselect.apply(obj); + + return false; + }); + } + + // upload + if (settings.upload) { + settings.upload.click(function(){ + uploader.cast('START'); + return false; + }); + } + }, + check : function() { + if (window.XMLHttpRequest) { + var xhr = new XMLHttpRequest(); + if (!!xhr.sendAsBinary || !!xhr.upload) return true; + } + + return false; + }, + upload : function(uploader, settings, files) { + if (uploader.request || !files.length) return false; + + uploader.cast('ON_START'); + + (function uploadNext() { + var file = files.shift(); + var req = uploader.request = new XMLHttpRequest(); + var data = ''; + var bndr = '--------------xe-boundary'+random(); + var bin = file.object.getAsBinary(); + var gap = 0; + + $.each(settings.params, function(key, val) { + data += '--'+bndr+'\r\n'; + data += 'Content-Disposition: form-data; name="'+key+'"\r\n\r\n'; + data += val+'\r\n'; + }); + + data += '--'+bndr+'\r\n'; + data += 'Content-Disposition: form-data; name="Filedata"; filename="'+file.name+'"\r\n'; + data += 'Content-Type: application/octet-stream\r\n\r\n'; + data += bin; + data += '\r\n'; + data += '--'+bndr+'--\r\n'; + + bin = null; + gap = data.length - file.object.fileSize; + + uploader.cast('ON_STARTONE', [file]); + + function nextOrFinish() { + if (files.length) { + uploadNext(); + } else { + setTimeout(function(){ uploader.cast('ON_FINISH') }, 0); + } + + uploader.cast('ON_FINISHONE', [file]); + } + + req.onreadystatechange = function() { + if (req.readyState == 1) { + file.status = 'UPLOADING'; + return; + } + if (req.readyState != 4) return; + + uploader.request = null; + + if (req.status == 200) { + file.loaded = file.size; + file.status = 'DONE'; + } else { + file.status = 'FAILED'; + } + nextOrFinish(); + }; + req.onerror = function(event) { + file.status = 'FAILED'; + nextOrFinish(); + }; + + if (req.upload) { + req.upload.onprogress = + req.upload.onload = + function(event){ + if (event.lengthComputable) { + file.loaded = Math.max(event.loaded - gap, 0); + uploader.cast('ON_PROGRESS', [file]); + } + }; + } + + req.open('POST', settings.url); + req.setRequestHeader('Content-Type', 'multipart/form-data; boundary='+bndr); + + if (req.sendAsBinary) req.sendAsBinary(data); + else req.send(data); + })(); + }, + stop : function(uploader, settings) { + if (uploader.request) { + uploader.request.abort(); + uploader.request = null; + } + + uploader.cast('ON_STOP'); + } +}; + +// Adobe Flash +runtimes.flash = { + version : 0, + object : null, + uploaders : [], + create : function(uploader, settings) { + var self = this; + var rand = random(); + var name = 'xe_flashuploader_object'+rand; + var swf = '/common/swf/uploader.swf'; + + if (!window.xe_flashuploaders) window.xe_flashuploaders = []; + + function make_button(event) { + var b = settings.browse.get(0); + var op = b.offsetParent; + var ow = b.offsetWidth; + var oh = b.offsetHeight; + var ot = b.offsetTop; + var ol = b.offsetLeft; + + if (typeof uploader.flashindex == 'undefined') { + uploader.flashindex = xe_flashuploaders.length; + + xe_flashuploaders.push({ + uploader : uploader, + settings : settings, + onselect : function(files) { + var old_length = uploader.files.length; + + $.each(files, function() { + uploader.files.push(new File(this, this.fileSize)) + }); + + uploader.cast('ON_SELECT', [uploader.files, old_length]); + }, + onprogress : function(fileInfo) {}, + oncomplete : function(fileInfo) {}, + onerror : function(fileInfo) {}, + oncancel : function(fileInfo) {} + }); + } + + var flash = $.browser.msie?window[name]:document[name]; + var css = {position:'absolute',width:ow+'px',height:oh+'px',left:ol+'px',top:ot+'px',margin:0,padding:0,overflow:'hidden'}; + + if (!flash) { + var div = $('
').css(css).appendTo(op); + div[0].innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + ''; + + flash = $.browser.msie?window[name]:document[name]; + } + + if (!uploader.flash) { + try { + var _settings = {}; + var pass_keys = ['filters','params','url']; + + $.each(settings, function(key,val){ + if ($.inArray(key, pass_keys) < 0) return true; + _settings[key] = val; + }); + + flash.setConfig(uploader.flashindex, _settings); + + uploader.flash = flash; + uploader.flash_box = $(flash).parents('div:first'); + } catch(e) { + return setTimeout(arguments.callee, 10); + } + } + if (uploader.flash) { + uploader.flash_box.css(css); + uploader.flash.setIndex(uploader.flashindex); + } + } + + // browse button + settings.browse.bind({mouseover:make_button,focus:make_button}); + + // upload + if (settings.upload) { + settings.upload.click(function(){ + uploader.cast('START'); + return false; + }); + } + + }, + check : function() { + var p = navigator.plugins, v; + + if (p && (v=p['Shockwave Flash'])) { + v = v.description; + } else { + try { + v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + } catch(e) { + v = "0.0"; + } + } + + v = v.match(/\d+/g); + v = parseFloat(v[0]+'.'+v[1]); + + if (!isNaN(v)) this.version = v; + + return (this.version >= 9); + }, + upload : function(uploader, settings, files) { + if (typeof uploader.flashindex == 'undefined') return false; + if (uploader.request || !files.length) return false; + + var g_uploader = xe_flashuploaders[uploader.flashindex]; + + uploader.cast('ON_START'); + + (function uploadNext(){ + var file = files.shift(); + uploader.flash.startUpload(file.object.index); + + uploader.cast('ON_STARTONE', [file]); + + function nextOrFinish() { + if (files.length) { + uploadNext(); + } else { + setTimeout(function(){ uploader.cast('ON_FINISH') }, 0); + } + + uploader.cast('ON_FINISHONE', [file]); + } + + file.status = 'UPLOADING'; + + g_uploader.onprogress = function(fileInfo){ + file.loaded = fileInfo.loaded; + uploader.cast('ON_PROGRESS', [file]); + }; + + g_uploader.oncomplete = function(fileInfo) { + file.loaded = fileInfo.loaded; + file.status = 'DONE'; + + nextOrFinish(); + }; + + g_uploader.onerror = function() { + file.status = 'FAILED'; + nextOrFinish(); + }; + })(); + }, + stop : function(uploader, settings) { + if (typeof uploader.flashindex == 'undefined') return false; + } +}; + +// HTML4 +runtimes.html4 = { + create : function(uploader, settings) { + var self = this; + var filter = []; + + // filter by extension + $.each(settings.filters, function(k,v){ filter=$.merge(filter,v.split(' ')) }); + + // when file is selected + function onselect() { + var ext = (this.value.match(/\.([^\.]+)$/)[1] || '').toLowerCase(); + + if ($.inArray(ext, filter) != -1) { + uploader.files.push(new File(this, -1)); + uploader.cast('ON_SELECT', [uploader.files, uploader.files.length - 1]); + } + + uploader.browseButton = null; + $(this).parent().remove(); + } + + function make_button(event) { + var op = this.offsetParent; + var ow = this.offsetWidth; + var oh = this.offsetHeight; + var ot = this.offsetTop; + var ol = this.offsetLeft; + + if (!uploader.browseButton) { + uploader.browseButton = $('
') + .appendTo(op) + .change(onselect); + uploader.browseButton.find('>input').css({cursor:'pointer',opacity:0}); + } + + uploader.browseButton.css({width:ow+'px', height:oh+'px', left:ol+'px', top:ot+'px', margin:0, padding:0}); + + if (event.type == 'focus') uploader.browseButton.find('>input').focus(); + } + + // browse button + settings.browse.bind({mouseover:make_button,focus:make_button}); + + // upload + if (settings.upload) { + settings.upload.click(function(){ + uploader.cast('START'); + return false; + }); + } + }, + check : function() { + return true; + }, + upload : function(uploader, settings, files) { + if (uploader.request || !files.length) return false; + + var css = {position:'absolute',width:'1px',height:'1px',left:'-100px',top:'-100px',overflow:'hidden'}; + + // callback + if (!window.callbacks) window.callbacks = {}; + + uploader.cast('ON_START'); + + (function uploadNext(){ + var file = files.shift(); + var id = 'tmp_upload_'+random(); + var iframe = $('').css(css).appendTo(document.documentElement); + var form = $('
').css(css).appendTo(document.documentElement); + + form.attr('action', settings.url); + + uploader.request = {iframe:iframe, form:form}; + + // set parameters + settings.params._callback = 'callbacks.'+id; // callback + $.each(settings.params, function(k,v) { + $('').val(v).appendTo(form); + }); + + // set status of a file + file.status = 'UPLOADING'; + + callbacks[id] = function(fileInfo) { + file.status = 'DONE'; + file.size = fileInfo.size; + file.loaded = fileInfo.size; + + form.find('>input[type=file]').remove(); + + uploader.cast('ON_FINISHONE', [file]); + + if (files.length) { + setTimeout(uploadNext, 0); + } else { + uploader.request = null; + uploader.cast('ON_FINISH'); + } + + setTimeout(function(){ + form.remove(); + iframe.remove(); + + delete callbacks[id]; + }, 0); + }; + + form.append(file.object).submit(); + })(); + }, + stop : function(uploader, settings) { + if (uploader.request) { + uploader.request.iframe.attr('src', 'about:blank').remove(); + uploader.request.form.remove(); + uploader.request = null; + } + } +}; + +// }}} runtimes + +var mimetypes = { + 'application/java-archive' : 'jar', + 'application/java-vm' : 'class', + 'application/javascript' : 'js', + 'application/msword' : 'doc dot', + 'application/pdf' : 'pdf', + 'application/octet-stream' : 'bin lha lzh iso dmg dist pkg exe', + 'application/postscript' : 'ai eps ps', + 'application/rtf' : 'rtf', + 'application/smil' : 'smi smil', + 'application/vnd.ms-excel' : 'xls xlm xla xlc xlt xlw', + 'application/vnd.openxmlformats': 'docx pptx xlsx', + 'application/vnd.ms-powerpoint' : 'ppt pps pot', + 'application/zip' : 'zip', + 'application/x-shockwave-flash' : 'swf swfl', + 'audio/mpeg' : 'mpga mpega mp2 mp3', + 'audio/x-wav' : 'wav', + 'image/bmp' : 'bmp', + 'image/gif' : 'gif', + 'image/jpeg' : 'jpeg jpg jpe', + 'image/png' : 'png', + 'image/svg+xml' : 'svg svgz', + 'image/tiff' : 'tiff tif', + 'text/html' : 'htm html xhtml', + 'text/plain' : 'asc txt text diff log', + 'video/mpeg' : 'mpeg mpg mpe', + 'video/quicktime' : 'qt mov', + 'video/x-flv' : 'flv', + 'video/x-ms-asf' : 'asf', + 'video/x-ms-wmv' : 'wmv', + 'video/x-msvideo' : 'avi' +}; + +function File(obj, filesize) { + if (runtime == 'html4') { + this.name = obj.value.match(/[^\\\/]+$/)[0]; + } else { + this.name = obj.name || obj.fileName; + } + + this.size = filesize; + this.loaded = 0; + this.status = 'QUEUED'; // QUEUED, UPLOADING, FAILED, DONE + this.object = obj; +}; + +function random() { + return Math.floor(Math.random()*80000+10000); +} + +})(jQuery); diff --git a/common/js/plugins/uploader/uploader.swf b/common/js/plugins/uploader/uploader.swf new file mode 100644 index 000000000..bcc4946df Binary files /dev/null and b/common/js/plugins/uploader/uploader.swf differ