You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							376 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							376 lines
						
					
					
						
							12 KiB
						
					
					
				| /**! | |
|  * AngularJS file upload/drop directive and service with progress and abort | |
|  * FileAPI Flash shim for old browsers not supporting FormData  | |
|  * @author  Danial  <danial.farid@gmail.com> | |
|  * @version 4.1.0 | |
|  */ | |
| 
 | |
| (function() { | |
| 
 | |
| var hasFlash = function() { | |
| 	try { | |
| 	  var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); | |
| 	  if (fo) return true; | |
| 	} catch(e) { | |
| 	  if (navigator.mimeTypes['application/x-shockwave-flash'] != undefined) return true; | |
| 	} | |
| 	return false; | |
| } | |
| 
 | |
| function patchXHR(fnName, newFn) { | |
| 	window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]); | |
| }; | |
| 
 | |
| if ((window.XMLHttpRequest && !window.FormData) || (window.FileAPI && FileAPI.forceLoad)) { | |
| 	var initializeUploadListener = function(xhr) { | |
| 		if (!xhr.__listeners) { | |
| 			if (!xhr.upload) xhr.upload = {}; | |
| 			xhr.__listeners = []; | |
| 			var origAddEventListener = xhr.upload.addEventListener; | |
| 			xhr.upload.addEventListener = function(t, fn, b) { | |
| 				xhr.__listeners[t] = fn; | |
| 				origAddEventListener && origAddEventListener.apply(this, arguments); | |
| 			}; | |
| 		} | |
| 	} | |
| 	 | |
| 	patchXHR('open', function(orig) { | |
| 		return function(m, url, b) { | |
| 			initializeUploadListener(this); | |
| 			this.__url = url; | |
| 			try { | |
| 				orig.apply(this, [m, url, b]); | |
| 			} catch (e) { | |
| 				if (e.message.indexOf('Access is denied') > -1) { | |
| 					this.__origError = e; | |
| 					orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]); | |
| 				} | |
| 			} | |
| 		} | |
| 	}); | |
| 
 | |
| 	patchXHR('getResponseHeader', function(orig) { | |
| 		return function(h) { | |
| 			return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h])); | |
| 		}; | |
| 	}); | |
| 
 | |
| 	patchXHR('getAllResponseHeaders', function(orig) { | |
| 		return function() { | |
| 			return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this)); | |
| 		} | |
| 	}); | |
| 
 | |
| 	patchXHR('abort', function(orig) { | |
| 		return function() { | |
| 			return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this)); | |
| 		} | |
| 	}); | |
| 
 | |
| 	patchXHR('setRequestHeader', function(orig) { | |
| 		return function(header, value) { | |
| 			if (header === '__setXHR_') { | |
| 				initializeUploadListener(this); | |
| 				var val = value(this); | |
| 				// fix for angular < 1.2.0 | |
| 				if (val instanceof Function) { | |
| 					val(this); | |
| 				} | |
| 			} else { | |
| 				this.__requestHeaders = this.__requestHeaders || {}; | |
| 				this.__requestHeaders[header] = value; | |
| 				orig.apply(this, arguments); | |
| 			} | |
| 		} | |
| 	}); | |
| 	 | |
| 	function redefineProp(xhr, prop, fn) { | |
| 		try { | |
| 			Object.defineProperty(xhr, prop, {get: fn}); | |
| 		} catch (e) {/*ignore*/} | |
| 	} | |
| 
 | |
| 	patchXHR('send', function(orig) { | |
| 		return function() { | |
| 			var xhr = this; | |
| 			if (arguments[0] && arguments[0].__isFileAPIShim) { | |
| 				var formData = arguments[0]; | |
| 				var config = { | |
| 					url: xhr.__url, | |
| 					jsonp: false, //removes the callback form param | |
| 					cache: true, //removes the ?fileapiXXX in the url | |
| 					complete: function(err, fileApiXHR) { | |
| 						xhr.__completed = true; | |
| 						if (!err && xhr.__listeners['load'])  | |
| 							xhr.__listeners['load']({type: 'load', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true}); | |
| 						if (!err && xhr.__listeners['loadend'])  | |
| 							xhr.__listeners['loadend']({type: 'loadend', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true}); | |
| 						if (err === 'abort' && xhr.__listeners['abort'])  | |
| 							xhr.__listeners['abort']({type: 'abort', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true}); | |
| 						if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function() {return (fileApiXHR.status == 0 && err && err !== 'abort') ? 500 : fileApiXHR.status}); | |
| 						if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function() {return fileApiXHR.statusText}); | |
| 						redefineProp(xhr, 'readyState', function() {return 4}); | |
| 						if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function() {return fileApiXHR.response}); | |
| 						var resp = fileApiXHR.responseText || (err && fileApiXHR.status == 0 && err !== 'abort' ? err : undefined); | |
| 						redefineProp(xhr, 'responseText', function() {return resp}); | |
| 						redefineProp(xhr, 'response', function() {return resp}); | |
| 						if (err) redefineProp(xhr, 'err', function() {return err}); | |
| 						xhr.__fileApiXHR = fileApiXHR; | |
| 						if (xhr.onreadystatechange) xhr.onreadystatechange(); | |
| 						if (xhr.onload) xhr.onload(); | |
| 					}, | |
| 					fileprogress: function(e) { | |
| 						e.target = xhr; | |
| 						xhr.__listeners['progress'] && xhr.__listeners['progress'](e); | |
| 						xhr.__total = e.total; | |
| 						xhr.__loaded = e.loaded; | |
| 						if (e.total === e.loaded) { | |
| 							// fix flash issue that doesn't call complete if there is no response text from the server   | |
| 							var _this = this | |
| 							setTimeout(function() { | |
| 								if (!xhr.__completed) { | |
| 									xhr.getAllResponseHeaders = function(){}; | |
| 									_this.complete(null, {status: 204, statusText: 'No Content'}); | |
| 								} | |
| 							}, FileAPI.noContentTimeout || 10000); | |
| 						} | |
| 					}, | |
| 					headers: xhr.__requestHeaders | |
| 				} | |
| 				config.data = {}; | |
| 				config.files = {} | |
| 				for (var i = 0; i < formData.data.length; i++) { | |
| 					var item = formData.data[i]; | |
| 					if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) { | |
| 						config.files[item.key] = item.val; | |
| 					} else { | |
| 						config.data[item.key] = item.val; | |
| 					} | |
| 				} | |
| 
 | |
| 				setTimeout(function() { | |
| 					if (!hasFlash()) { | |
| 						throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; | |
| 					} | |
| 					xhr.__fileApiXHR = FileAPI.upload(config); | |
| 				}, 1); | |
| 			} else { | |
| 				if (this.__origError) { | |
| 					throw this.__origError; | |
| 				} | |
| 				orig.apply(xhr, arguments); | |
| 			} | |
| 		} | |
| 	}); | |
| 	window.XMLHttpRequest.__isFileAPIShim = true; | |
| 
 | |
| 	function isInputTypeFile(elem) { | |
| 		return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file'; | |
| 	} | |
| 	 | |
| 	window.FormData = FormData = function() { | |
| 		return { | |
| 			append: function(key, val, name) { | |
| 				if (val.__isFileAPIBlobShim) { | |
| 					val = val.data[0]; | |
| 				} | |
| 				this.data.push({ | |
| 					key: key, | |
| 					val: val, | |
| 					name: name | |
| 				}); | |
| 			}, | |
| 			data: [], | |
| 			__isFileAPIShim: true | |
| 		}; | |
| 	}; | |
| 
 | |
| 	window.Blob = Blob = function(b) { | |
| 		return { | |
| 			data: b, | |
| 			__isFileAPIBlobShim: true | |
| 		}; | |
| 	}; | |
| 
 | |
| 	(function () { | |
| 		//load FileAPI | |
| 		if (!window.FileAPI) { | |
| 			window.FileAPI = {}; | |
| 		} | |
| 		if (FileAPI.forceLoad) { | |
| 			FileAPI.html5 = false; | |
| 		} | |
| 		 | |
| 		if (!FileAPI.upload) { | |
| 			var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src; | |
| 			if (window.FileAPI.jsUrl) { | |
| 				jsUrl = window.FileAPI.jsUrl; | |
| 			} else if (window.FileAPI.jsPath) { | |
| 				basePath = window.FileAPI.jsPath; | |
| 			} else { | |
| 				for (i = 0; i < allScripts.length; i++) { | |
| 					src = allScripts[i].src; | |
| 					index = src.search(/\/ng\-file\-upload[\-a-zA-z0-9\.]*\.js/) | |
| 					if (index > -1) { | |
| 						basePath = src.substring(0, index + 1); | |
| 						break; | |
| 					} | |
| 				} | |
| 			} | |
| 
 | |
| 			if (FileAPI.staticPath == null) FileAPI.staticPath = basePath; | |
| 			script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js'); | |
| 			document.getElementsByTagName('head')[0].appendChild(script); | |
| 			FileAPI.hasFlash = hasFlash(); | |
| 		} | |
| 	})(); | |
| 	 | |
| 	FileAPI.ngfFixIE = function(elem, createFileElemFn, bindAttr, changeFn, resetModel) { | |
| 		if (!hasFlash()) { | |
| 			throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; | |
| 		} | |
| 		var makeFlashInput = function(evt) { | |
| 			if (elem.attr('disabled')) { | |
| 				elem.__ngf_elem__.removeClass('js-fileapi-wrapper'); | |
| 			} else { | |
| 				var fileElem = elem.__ngf_elem__; | |
| 				if (!fileElem) { | |
| 					fileElem = elem.__ngf_elem__ = createFileElemFn(); | |
| 					fileElem.addClass('js-fileapi-wrapper'); | |
| 					if (!isInputTypeFile(elem)) { | |
| //						if (fileElem.parent().css('position') === '' || fileElem.parent().css('position') === 'static') { | |
| //							fileElem.parent().css('position', 'relative'); | |
| //						} | |
| //						elem.parent()[0].insertBefore(fileElem[0], elem[0]); | |
| //						elem.css('overflow', 'hidden'); | |
| 					} | |
| 					setTimeout(function() { | |
| 						fileElem.bind('mouseenter', makeFlashInput); | |
| 					}, 10); | |
| 					fileElem.bind('change', function(evt) { | |
| 				    	fileApiChangeFn.apply(this, [evt]); | |
| 						changeFn.apply(this, [evt]); | |
| //						alert('change' +  evt); | |
| 					}); | |
| 				} else { | |
| 					bindAttr(elem.__ngf_elem__); | |
| 				} | |
| 				if (!isInputTypeFile(elem)) { | |
| 					fileElem.css('position', 'absolute') | |
| 							.css('top', getOffset(elem[0]).top + 'px').css('left', getOffset(elem[0]).left + 'px') | |
| 							.css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px') | |
| 							.css('filter', 'alpha(opacity=0)').css('display', elem.css('display')) | |
| 							.css('overflow', 'hidden').css('z-index', '900000'); | |
| 				} | |
| 			} | |
| 			function getOffset(obj) { | |
| 			    var left, top; | |
| 			    left = top = 0; | |
| 			    if (obj.offsetParent) { | |
| 			        do { | |
| 			            left += obj.offsetLeft; | |
| 			            top  += obj.offsetTop; | |
| 			        } while (obj = obj.offsetParent); | |
| 			    } | |
| 			    return { | |
| 			        left : left, | |
| 			        top : top | |
| 			    }; | |
| 			}; | |
| 		}; | |
| 
 | |
| 		elem.bind('mouseenter', makeFlashInput); | |
| 
 | |
| 		var fileApiChangeFn = function(evt) { | |
| 			var files = FileAPI.getFiles(evt); | |
| 			//just a double check for #233 | |
| 			for (var i = 0; i < files.length; i++) { | |
| 				if (files[i].size === undefined) files[i].size = 0; | |
| 				if (files[i].name === undefined) files[i].name = 'file'; | |
| 				if (files[i].type === undefined) files[i].type = 'undefined'; | |
| 			} | |
| 			if (!evt.target) { | |
| 				evt.target = {}; | |
| 			} | |
| 			evt.target.files = files; | |
| 			// if evt.target.files is not writable use helper field | |
| 			if (evt.target.files != files) { | |
| 				evt.__files_ = files; | |
| 			} | |
| 			(evt.__files_ || evt.target.files).item = function(i) { | |
| 				return (evt.__files_ || evt.target.files)[i] || null; | |
| 			}; | |
| 		}; | |
| 	}; | |
| 
 | |
| 	FileAPI.disableFileInput = function(elem, disable) { | |
| 		if (disable) { | |
| 			elem.removeClass('js-fileapi-wrapper') | |
| 		} else { | |
| 			elem.addClass('js-fileapi-wrapper'); | |
| 		} | |
| 	}; | |
| } | |
| 
 | |
| 
 | |
| if (!window.FileReader) { | |
| 	window.FileReader = function() { | |
| 		var _this = this, loadStarted = false; | |
| 		this.listeners = {}; | |
| 		this.addEventListener = function(type, fn) { | |
| 			_this.listeners[type] = _this.listeners[type] || []; | |
| 			_this.listeners[type].push(fn); | |
| 		}; | |
| 		this.removeEventListener = function(type, fn) { | |
| 			_this.listeners[type] && _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1); | |
| 		}; | |
| 		this.dispatchEvent = function(evt) { | |
| 			var list = _this.listeners[evt.type]; | |
| 			if (list) { | |
| 				for (var i = 0; i < list.length; i++) { | |
| 					list[i].call(_this, evt); | |
| 				} | |
| 			} | |
| 		}; | |
| 		this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null; | |
| 
 | |
| 		var constructEvent = function(type, evt) { | |
| 			var e = {type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error}; | |
| 			if (evt.result != null) e.target.result = evt.result; | |
| 			return e; | |
| 		}; | |
| 		var listener = function(evt) { | |
| 			if (!loadStarted) { | |
| 				loadStarted = true; | |
| 				_this.onloadstart && _this.onloadstart(constructEvent('loadstart', evt)); | |
| 			} | |
| 			if (evt.type === 'load') { | |
| 				_this.onloadend && _this.onloadend(constructEvent('loadend', evt)); | |
| 				var e = constructEvent('load', evt); | |
| 				_this.onload && _this.onload(e); | |
| 				_this.dispatchEvent(e); | |
| 			} else if (evt.type === 'progress') { | |
| 				var e = constructEvent('progress', evt); | |
| 				_this.onprogress && _this.onprogress(e); | |
| 				_this.dispatchEvent(e); | |
| 			} else { | |
| 				var e = constructEvent('error', evt); | |
| 				_this.onerror && _this.onerror(e); | |
| 				_this.dispatchEvent(e); | |
| 			} | |
| 		}; | |
| 		this.readAsArrayBuffer = function(file) { | |
| 			FileAPI.readAsBinaryString(file, listener); | |
| 		} | |
| 		this.readAsBinaryString = function(file) { | |
| 			FileAPI.readAsBinaryString(file, listener); | |
| 		} | |
| 		this.readAsDataURL = function(file) { | |
| 			FileAPI.readAsDataURL(file, listener); | |
| 		} | |
| 		this.readAsText = function(file) { | |
| 			FileAPI.readAsText(file, listener); | |
| 		} | |
| 	} | |
| } | |
| })();
 | |
| 
 |