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