ng-file-upload-haikulearning
Version:
An AngularJS directive for file upload using HTML5 with FileAPI polyfill for unsupported browsers
231 lines (215 loc) • 8.01 kB
JavaScript
/**!
* AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort,
* progress, resize, thumbnail, preview, validation and CORS
* FileAPI Flash shim for old browsers not supporting FormData
* @author Danial <danial.farid@gmail.com>
* @version <%= pkg.version %>
*/
(function () {
/** @namespace FileAPI.noContentTimeout */
function patchXHR(fnName, newFn) {
window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
}
function redefineProp(xhr, prop, fn) {
try {
Object.defineProperty(xhr, prop, {get: fn});
} catch (e) {/*ignore*/
}
}
if (!window.FileAPI) {
window.FileAPI = {};
}
if (!window.XMLHttpRequest) {
throw 'AJAX is not supported. XMLHttpRequest is not defined.';
}
FileAPI.shouldLoad = !window.FormData || FileAPI.forceLoad;
if (FileAPI.shouldLoad) {
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) {
xhr.__listeners[t] = fn;
if (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);
}
};
});
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) {
if (err && angular.isString(err) && err.indexOf('#2174') !== -1) {
// this error seems to be fine the file is being uploaded properly.
err = null;
}
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();
},
progress: function (e) {
e.target = xhr;
if (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 (!FileAPI.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;
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
};
};
}
})();