/**!
|
* AngularJS file upload/drop directive and service with progress and abort
|
* @author Danial <danial.farid@gmail.com>
|
* @version 3.1.2
|
*/
|
(function() {
|
|
function patchXHR(fnName, newFn) {
|
window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
|
}
|
|
if (window.XMLHttpRequest && !window.XMLHttpRequest.__isFileAPIShim) {
|
patchXHR('setRequestHeader', function(orig) {
|
return function(header, value) {
|
if (header === '__setXHR_') {
|
var val = value(this);
|
// fix for angular < 1.2.0
|
if (val instanceof Function) {
|
val(this);
|
}
|
} else {
|
orig.apply(this, arguments);
|
}
|
}
|
});
|
}
|
|
var angularFileUpload = angular.module('angularFileUpload', []);
|
|
angularFileUpload.version = '3.1.2';
|
angularFileUpload.service('$upload', ['$http', '$q', '$timeout', function($http, $q, $timeout) {
|
function sendHttp(config) {
|
config.method = config.method || 'POST';
|
config.headers = config.headers || {};
|
config.transformRequest = config.transformRequest || function(data, headersGetter) {
|
if (window.ArrayBuffer && data instanceof window.ArrayBuffer) {
|
return data;
|
}
|
return $http.defaults.transformRequest[0](data, headersGetter);
|
};
|
var deferred = $q.defer();
|
var promise = deferred.promise;
|
|
config.headers['__setXHR_'] = function() {
|
return function(xhr) {
|
if (!xhr) return;
|
config.__XHR = xhr;
|
config.xhrFn && config.xhrFn(xhr);
|
xhr.upload.addEventListener('progress', function(e) {
|
e.config = config;
|
deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function(){promise.progress_fn(e)});
|
}, false);
|
//fix for firefox not firing upload progress end, also IE8-9
|
xhr.upload.addEventListener('load', function(e) {
|
if (e.lengthComputable) {
|
e.config = config;
|
deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function(){promise.progress_fn(e)});
|
}
|
}, false);
|
};
|
};
|
|
$http(config).then(function(r){deferred.resolve(r)}, function(e){deferred.reject(e)}, function(n){deferred.notify(n)});
|
|
promise.success = function(fn) {
|
promise.then(function(response) {
|
fn(response.data, response.status, response.headers, config);
|
});
|
return promise;
|
};
|
|
promise.error = function(fn) {
|
promise.then(null, function(response) {
|
fn(response.data, response.status, response.headers, config);
|
});
|
return promise;
|
};
|
|
promise.progress = function(fn) {
|
promise.progress_fn = fn;
|
promise.then(null, null, function(update) {
|
fn(update);
|
});
|
return promise;
|
};
|
promise.abort = function() {
|
if (config.__XHR) {
|
$timeout(function() {
|
config.__XHR.abort();
|
});
|
}
|
return promise;
|
};
|
promise.xhr = function(fn) {
|
config.xhrFn = (function(origXhrFn) {
|
return function() {
|
origXhrFn && origXhrFn.apply(promise, arguments);
|
fn.apply(promise, arguments);
|
}
|
})(config.xhrFn);
|
return promise;
|
};
|
|
return promise;
|
}
|
|
this.upload = function(config) {
|
config.headers = config.headers || {};
|
config.headers['Content-Type'] = undefined;
|
var origTransformRequest = config.transformRequest;
|
config.transformRequest = config.transformRequest ?
|
(Object.prototype.toString.call(config.transformRequest) === '[object Array]' ?
|
config.transformRequest : [config.transformRequest]) : [];
|
config.transformRequest.push(function(data, headerGetter) {
|
var formData = new FormData();
|
var allFields = {};
|
for (var key in config.fields) allFields[key] = config.fields[key];
|
if (data) allFields['data'] = data;
|
|
if (config.formDataAppender) {
|
for (var key in allFields) {
|
config.formDataAppender(formData, key, allFields[key]);
|
}
|
} else {
|
for (var key in allFields) {
|
var val = allFields[key];
|
if (val !== undefined) {
|
if (Object.prototype.toString.call(val) === '[object String]') {
|
formData.append(key, val);
|
} else {
|
if (config.sendObjectsAsJsonBlob && typeof val === 'object') {
|
formData.append(key, new Blob([val], { type: 'application/json' }));
|
} else {
|
formData.append(key, JSON.stringify(val));
|
}
|
}
|
}
|
}
|
}
|
|
if (config.file != null) {
|
var fileFormName = config.fileFormDataName || 'file';
|
|
if (Object.prototype.toString.call(config.file) === '[object Array]') {
|
var isFileFormNameString = Object.prototype.toString.call(fileFormName) === '[object String]';
|
for (var i = 0; i < config.file.length; i++) {
|
formData.append(isFileFormNameString ? fileFormName : fileFormName[i], config.file[i],
|
(config.fileName && config.fileName[i]) || config.file[i].name);
|
}
|
} else {
|
formData.append(fileFormName, config.file, config.fileName || config.file.name);
|
}
|
}
|
return formData;
|
});
|
|
return sendHttp(config);
|
};
|
|
this.http = function(config) {
|
return sendHttp(config);
|
};
|
}]);
|
|
angularFileUpload.directive('ngFileSelect', [ '$parse', '$timeout', '$compile',
|
function($parse, $timeout, $compile) { return {
|
restrict: 'AEC',
|
require:'?ngModel',
|
link: function(scope, elem, attr, ngModel) {
|
handleFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile);
|
}
|
}}]);
|
|
function handleFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile) {
|
function isInputTypeFile() {
|
return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file';
|
}
|
|
var watchers = [];
|
|
function watchForRecompile(attrVal) {
|
$timeout(function() {
|
if (elem.parent().length) {
|
watchers.push(scope.$watch(attrVal, function(val, oldVal) {
|
if (val != oldVal) {
|
recompileElem();
|
}
|
}));
|
}
|
});
|
}
|
|
function recompileElem() {
|
var clone = elem.clone();
|
if (elem.attr('__afu_gen__')) {
|
angular.element(document.getElementById(elem.attr('id').substring(1))).remove();
|
}
|
if (elem.parent().length) {
|
for (var i = 0; i < watchers.length; i++) {
|
watchers[i]();
|
}
|
elem.replaceWith(clone);
|
$compile(clone)(scope);
|
}
|
return clone;
|
}
|
|
function bindAttr(bindAttr, attrName) {
|
if (bindAttr) {
|
watchForRecompile(bindAttr);
|
var val = $parse(bindAttr)(scope);
|
if (val) {
|
elem.attr(attrName, val);
|
attr[attrName] = val;
|
} else {
|
elem.attr(attrName, null);
|
delete attr[attrName];
|
}
|
}
|
}
|
|
bindAttr(attr.ngMultiple, 'multiple');
|
bindAttr(attr.ngAccept, 'ng-accept');
|
bindAttr(attr.ngCapture, 'capture');
|
|
if (attr['ngFileSelect'] != '') {
|
attr.ngFileChange = attr.ngFileSelect;
|
}
|
|
function onChangeFn(evt) {
|
var files = [], fileList, i;
|
fileList = evt.__files_ || (evt.target && evt.target.files);
|
updateModel(fileList, attr, ngModel, scope, evt);
|
};
|
|
var fileElem = elem;
|
if (!isInputTypeFile()) {
|
fileElem = angular.element('<input type="file">')
|
if (elem.attr('multiple')) fileElem.attr('multiple', elem.attr('multiple'));
|
if (elem.attr('accept')) fileElem.attr('accept', elem.attr('accept'));
|
if (elem.attr('capture')) fileElem.attr('capture', elem.attr('capture'));
|
for (var key in attr) {
|
if (key.indexOf('inputFile') == 0) {
|
var name = key.substring('inputFile'.length);
|
name = name[0].toLowerCase() + name.substring(1);
|
fileElem.attr(name, attr[key]);
|
}
|
}
|
|
fileElem.css('width', '0px').css('height', '0px').css('position', 'absolute').css('padding', 0).css('margin', 0)
|
.css('overflow', 'hidden').attr('tabindex', '-1').css('opacity', 0).attr('__afu_gen__', true);
|
elem.attr('__refElem__', true);
|
fileElem[0].__refElem__ = elem[0];
|
elem.parent()[0].insertBefore(fileElem[0], elem[0])
|
elem.css('overflow', 'hidden');
|
elem.bind('click', function(e) {
|
if (!resetAndClick(e)) {
|
fileElem[0].click();
|
}
|
});
|
} else {
|
elem.bind('click', resetAndClick);
|
}
|
|
function resetAndClick(evt) {
|
if (fileElem[0].value != null && fileElem[0].value != '') {
|
fileElem[0].value = null;
|
// IE 11 already fires change event when you set the value to null
|
if (navigator.userAgent.indexOf("Trident/7") === -1) {
|
onChangeFn({target: {files: []}});
|
}
|
}
|
// if this is manual click trigger we don't need to reset again
|
if (!elem.attr('__afu_clone__')) {
|
// fix for IE10 cannot set the value of the input to null programmatically by cloning and replacing input
|
// IE 11 setting the value to null event will be fired after file change clearing the selected file so
|
// we just recreate the element for IE 11 as well
|
if (navigator.appVersion.indexOf("MSIE 10") !== -1 || navigator.userAgent.indexOf("Trident/7") !== -1) {
|
var clone = recompileElem();
|
clone.attr('__afu_clone__', true);
|
clone[0].click();
|
evt.preventDefault();
|
evt.stopPropagation();
|
return true;
|
}
|
} else {
|
elem.attr('__afu_clone__', null);
|
}
|
}
|
|
fileElem.bind('change', onChangeFn);
|
|
elem.on('$destroy', function() {
|
for (var i = 0; i < watchers.length; i++) {
|
watchers[i]();
|
}
|
if (elem[0] != fileElem[0]) fileElem.remove();
|
});
|
|
watchers.push(scope.$watch(attr.ngModel, function(val, oldVal) {
|
if (val != oldVal && (val == null || !val.length)) {
|
if (navigator.appVersion.indexOf("MSIE 10") !== -1) {
|
recompileElem();
|
} else {
|
fileElem[0].value = null;
|
}
|
}
|
}));
|
|
function updateModel(fileList, attr, ngModel, scope, evt) {
|
var files = [], rejFiles = [];
|
var accept = $parse(attr.ngAccept)(scope);
|
var regexp = angular.isString(accept) && accept ? new RegExp(globStringToRegex(accept), 'gi') : null;
|
var acceptFn = regexp ? null : attr.ngAccept;
|
|
for (var i = 0; i < fileList.length; i++) {
|
var file = fileList.item(i);
|
if ((!regexp || file.type.match(regexp) || (file.name != null && file.name.match(regexp))) &&
|
(!acceptFn || $parse(acceptFn)(scope, {$file: file, $event: evt}))) {
|
files.push(file);
|
} else {
|
rejFiles.push(file);
|
}
|
}
|
$timeout(function() {
|
if (ngModel) {
|
$parse(attr.ngModel).assign(scope, files);
|
ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? '' : files);
|
if (attr.ngModelRejected) {
|
$parse(attr.ngModelRejected).assign(scope, rejFiles);
|
}
|
}
|
if (attr.ngFileChange && attr.ngFileChange != "") {
|
$parse(attr.ngFileChange)(scope, {
|
$files: files,
|
$rejectedFiles: rejFiles,
|
$event: evt
|
});
|
}
|
});
|
}
|
}
|
|
angularFileUpload.directive('ngFileDrop', [ '$parse', '$timeout', '$location', function($parse, $timeout, $location) { return {
|
restrict: 'AEC',
|
require:'?ngModel',
|
link: function(scope, elem, attr, ngModel) {
|
handleDrop(scope, elem, attr, ngModel, $parse, $timeout, $location);
|
}
|
}}]);
|
|
angularFileUpload.directive('ngNoFileDrop', function() {
|
return function(scope, elem, attr) {
|
if (dropAvailable()) elem.css('display', 'none')
|
}
|
});
|
|
//for backward compatibility
|
angularFileUpload.directive('ngFileDropAvailable', [ '$parse', '$timeout', function($parse, $timeout) {
|
return function(scope, elem, attr) {
|
if (dropAvailable()) {
|
var fn = $parse(attr['ngFileDropAvailable']);
|
$timeout(function() {
|
fn(scope);
|
});
|
}
|
}
|
}]);
|
|
function handleDrop(scope, elem, attr, ngModel, $parse, $timeout, $location) {
|
var available = dropAvailable();
|
if (attr['dropAvailable']) {
|
$timeout(function() {
|
scope.dropAvailable ? scope.dropAvailable.value = available : scope.dropAvailable = available;
|
});
|
}
|
if (!available) {
|
if ($parse(attr.hideOnDropNotAvailable)(scope) != false) {
|
elem.css('display', 'none');
|
}
|
return;
|
}
|
var leaveTimeout = null;
|
var stopPropagation = $parse(attr.stopPropagation)(scope);
|
var dragOverDelay = 1;
|
var accept = $parse(attr.ngAccept)(scope) || attr.accept;
|
var regexp = angular.isString(accept) && accept ? new RegExp(globStringToRegex(accept), 'gi') : null;
|
var acceptFn = regexp ? null : attr.ngAccept;
|
var actualDragOverClass;
|
elem[0].addEventListener('dragover', function(evt) {
|
evt.preventDefault();
|
if (stopPropagation) evt.stopPropagation();
|
// handling dragover events from the Chrome download bar
|
if (navigator.userAgent.indexOf("Chrome") > -1) {
|
var b = evt.dataTransfer.effectAllowed;
|
evt.dataTransfer.dropEffect = ('move' === b || 'linkMove' === b) ? 'move' : 'copy';
|
}
|
$timeout.cancel(leaveTimeout);
|
if (!scope.actualDragOverClass) {
|
actualDragOverClass = calculateDragOverClass(scope, attr, evt);
|
}
|
elem.addClass(actualDragOverClass);
|
}, false);
|
elem[0].addEventListener('dragenter', function(evt) {
|
evt.preventDefault();
|
if (stopPropagation) evt.stopPropagation();
|
}, false);
|
elem[0].addEventListener('dragleave', function(evt) {
|
leaveTimeout = $timeout(function() {
|
elem.removeClass(actualDragOverClass);
|
actualDragOverClass = null;
|
}, dragOverDelay || 1);
|
}, false);
|
if (attr['ngFileDrop'] != '') {
|
attr.ngFileChange = scope.ngFileDrop;
|
}
|
elem[0].addEventListener('drop', function(evt) {
|
evt.preventDefault();
|
if (stopPropagation) evt.stopPropagation();
|
elem.removeClass(actualDragOverClass);
|
actualDragOverClass = null;
|
extractFiles(evt, function(files, rejFiles) {
|
$timeout(function() {
|
if (ngModel) {
|
$parse(attr.ngModel).assign(scope, files);
|
ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? '' : files);
|
}
|
if (attr['ngModelRejected']) {
|
if (scope[attr.ngModelRejected]) {
|
$parse(attr.ngModelRejected).assign(scope, rejFiles);
|
}
|
}
|
});
|
$timeout(function() {
|
$parse(attr.ngFileChange)(scope, {
|
$files: files,
|
$rejectedFiles: rejFiles,
|
$event: evt
|
});
|
});
|
}, $parse(attr.allowDir)(scope) != false, attr.multiple || $parse(attr.ngMultiple)(scope));
|
}, false);
|
|
function calculateDragOverClass(scope, attr, evt) {
|
var valid = true;
|
if (regexp || acceptFn) {
|
var items = evt.dataTransfer.items;
|
if (items != null) {
|
for (var i = 0 ; i < items.length && valid; i++) {
|
valid = valid && (items[i].kind == 'file' || items[i].kind == '') &&
|
((acceptFn && $parse(acceptFn)(scope, {$file: items[i], $event: evt})) ||
|
(regexp && (items[i].type != null && items[i].type.match(regexp)) ||
|
(items[i].name != null && items[i].name.match(regexp))));
|
}
|
}
|
}
|
var clazz = $parse(attr.dragOverClass)(scope, {$event : evt});
|
if (clazz) {
|
if (clazz.delay) dragOverDelay = clazz.delay;
|
if (clazz.accept) clazz = valid ? clazz.accept : clazz.reject;
|
}
|
return clazz || attr['dragOverClass'] || 'dragover';
|
}
|
|
function extractFiles(evt, callback, allowDir, multiple) {
|
var files = [], rejFiles = [], items = evt.dataTransfer.items, processing = 0;
|
|
function addFile(file) {
|
if ((!regexp || file.type.match(regexp) || (file.name != null && file.name.match(regexp))) &&
|
(!acceptFn || $parse(acceptFn)(scope, {$file: file, $event: evt}))) {
|
files.push(file);
|
} else {
|
rejFiles.push(file);
|
}
|
}
|
|
if (items && items.length > 0 && $location.protocol() != 'file') {
|
for (var i = 0; i < items.length; i++) {
|
if (items[i].webkitGetAsEntry && items[i].webkitGetAsEntry() && items[i].webkitGetAsEntry().isDirectory) {
|
var entry = items[i].webkitGetAsEntry();
|
if (entry.isDirectory && !allowDir) {
|
continue;
|
}
|
if (entry != null) {
|
traverseFileTree(files, entry);
|
}
|
} else {
|
var f = items[i].getAsFile();
|
if (f != null) addFile(f);
|
}
|
if (!multiple && files.length > 0) break;
|
}
|
} else {
|
var fileList = evt.dataTransfer.files;
|
if (fileList != null) {
|
for (var i = 0; i < fileList.length; i++) {
|
addFile(fileList.item(i));
|
if (!multiple && files.length > 0) break;
|
}
|
}
|
}
|
var delays = 0;
|
(function waitForProcess(delay) {
|
$timeout(function() {
|
if (!processing) {
|
if (!multiple && files.length > 1) {
|
var i = 0;
|
while (files[i].type == 'directory') i++;
|
files = [files[i]];
|
}
|
callback(files, rejFiles);
|
} else {
|
if (delays++ * 10 < 20 * 1000) {
|
waitForProcess(10);
|
}
|
}
|
}, delay || 0)
|
})();
|
|
function traverseFileTree(files, entry, path) {
|
if (entry != null) {
|
if (entry.isDirectory) {
|
var filePath = (path || '') + entry.name;
|
addFile({name: entry.name, type: 'directory', path: filePath});
|
var dirReader = entry.createReader();
|
var entries = [];
|
processing++;
|
var readEntries = function() {
|
dirReader.readEntries(function(results) {
|
try {
|
if (!results.length) {
|
for (var i = 0; i < entries.length; i++) {
|
traverseFileTree(files, entries[i], (path ? path : '') + entry.name + '/');
|
}
|
processing--;
|
} else {
|
entries = entries.concat(Array.prototype.slice.call(results || [], 0));
|
readEntries();
|
}
|
} catch (e) {
|
processing--;
|
console.error(e);
|
}
|
}, function() {
|
processing--;
|
});
|
};
|
readEntries();
|
} else {
|
processing++;
|
entry.file(function(file) {
|
try {
|
processing--;
|
file.path = (path ? path : '') + file.name;
|
addFile(file);
|
} catch (e) {
|
processing--;
|
console.error(e);
|
}
|
}, function(e) {
|
processing--;
|
});
|
}
|
}
|
}
|
}
|
}
|
|
function dropAvailable() {
|
var div = document.createElement('div');
|
return ('draggable' in div) && ('ondrop' in div);
|
}
|
|
function globStringToRegex(str) {
|
if (str.length > 2 && str[0] === '/' && str[str.length -1] === '/') {
|
return str.substring(1, str.length - 1);
|
}
|
var split = str.split(','), result = '';
|
if (split.length > 1) {
|
for (var i = 0; i < split.length; i++) {
|
result += '(' + globStringToRegex(split[i]) + ')';
|
if (i < split.length - 1) {
|
result += '|'
|
}
|
}
|
} else {
|
if (str.indexOf('.') == 0) {
|
str= '*' + str;
|
}
|
result = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + '-]', 'g'), '\\$&') + '$';
|
result = result.replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
|
}
|
return result;
|
}
|
|
var ngFileUpload = angular.module('ngFileUpload', []);
|
|
for (var key in angularFileUpload) {
|
ngFileUpload[key] = angularFileUpload[key];
|
}
|
|
})();
|
|
/**!
|
* 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 3.1.2
|
*/
|
|
(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;
|
|
var addFlash = function(elem) {
|
if (!hasFlash()) {
|
throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
|
}
|
var el = angular.element(elem);
|
if (!el.attr('disabled')) {
|
var hasFileSelect = false;
|
for (var i = 0; i < el[0].attributes.length; i++) {
|
var attrib = el[0].attributes[i];
|
if (attrib.name.indexOf('file-select') !== -1) {
|
hasFileSelect = true;
|
break;
|
}
|
}
|
if (!el.hasClass('js-fileapi-wrapper') && (hasFileSelect || el.attr('__afu_gen__') != null)) {
|
|
el.addClass('js-fileapi-wrapper');
|
if (el.attr('__afu_gen__') != null) {
|
var ref = (el[0].__refElem__ && angular.element(el[0].__refElem__)) || el;
|
while (ref && !ref.attr('__refElem__')) {
|
ref = angular.element(ref[0].nextSibling);
|
}
|
ref.bind('mouseover', function() {
|
if (el.parent().css('position') === '' || el.parent().css('position') === 'static') {
|
el.parent().css('position', 'relative');
|
}
|
el.css('position', 'absolute').css('top', ref[0].offsetTop + 'px').css('left', ref[0].offsetLeft + 'px')
|
.css('width', ref[0].offsetWidth + 'px').css('height', ref[0].offsetHeight + 'px')
|
.css('padding', ref.css('padding')).css('margin', ref.css('margin')).css('filter', 'alpha(opacity=0)');
|
ref.attr('onclick', '');
|
el.css('z-index', '1000');
|
});
|
}
|
}
|
}
|
};
|
var changeFnWrapper = function(fn) {
|
return 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;
|
}
|
if (fn) fn.apply(this, [evt]);
|
};
|
};
|
var isFileChange = function(elem, e) {
|
return (e.toLowerCase() === 'change' || e.toLowerCase() === 'onchange') && elem.getAttribute('type') == 'file';
|
}
|
if (HTMLInputElement.prototype.addEventListener) {
|
HTMLInputElement.prototype.addEventListener = (function(origAddEventListener) {
|
return function(e, fn, b, d) {
|
if (isFileChange(this, e)) {
|
addFlash(this);
|
origAddEventListener.apply(this, [e, changeFnWrapper(fn), b, d]);
|
} else {
|
origAddEventListener.apply(this, [e, fn, b, d]);
|
}
|
}
|
})(HTMLInputElement.prototype.addEventListener);
|
}
|
if (HTMLInputElement.prototype.attachEvent) {
|
HTMLInputElement.prototype.attachEvent = (function(origAttachEvent) {
|
return function(e, fn) {
|
if (isFileChange(this, e)) {
|
addFlash(this);
|
if (window.jQuery) {
|
// fix for #281 jQuery on IE8
|
angular.element(this).bind('change', changeFnWrapper(null));
|
} else {
|
origAttachEvent.apply(this, [e, changeFnWrapper(fn)]);
|
}
|
} else {
|
origAttachEvent.apply(this, [e, fn]);
|
}
|
}
|
})(HTMLInputElement.prototype.attachEvent);
|
}
|
|
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(/\/angular\-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.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);
|
}
|
}
|
}
|
})();
|