123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /**!
- * AngularJS file upload/drop directive and service with progress and abort
- * @author Danial <danial.farid@gmail.com>
- * @version 3.2.4
- */
- (function () {
- var key, i;
- 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.2.4';
- 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;
- config.transformRequest = config.transformRequest ?
- (Object.prototype.toString.call(config.transformRequest) === '[object Array]' ?
- config.transformRequest : [config.transformRequest]) : [];
- config.transformRequest.push(function (data) {
- var formData = new FormData();
- var allFields = {};
- for (key in config.fields) {
- if (config.fields.hasOwnProperty(key)) {
- allFields[key] = config.fields[key];
- }
- }
- if (data) allFields['data'] = data;
- if (config.formDataAppender) {
- for (key in allFields) {
- if (allFields.hasOwnProperty(key)) {
- config.formDataAppender(formData, key, allFields[key]);
- }
- }
- } else {
- for (key in allFields) {
- if (allFields.hasOwnProperty(key)) {
- 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 (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) {
- linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile);
- }
- }
- }]);
- function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile) {
- function isInputTypeFile() {
- return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file';
- }
- var isUpdating = false;
- function changeFn(evt) {
- if (!isUpdating) {
- isUpdating = true;
- try {
- var fileList = evt.__files_ || (evt.target && evt.target.files);
- var files = [], rejFiles = [];
- var accept = $parse(attr.ngAccept);
- for (i = 0; i < fileList.length; i++) {
- var file = fileList.item(i);
- if (isAccepted(scope, accept, file, evt)) {
- files.push(file);
- } else {
- rejFiles.push(file);
- }
- }
- updateModel($parse, $timeout, scope, ngModel, attr,
- attr.ngFileChange || attr.ngFileSelect, files, rejFiles, evt);
- if (files.length == 0) evt.target.value = files;
- if (evt.target && evt.target.getAttribute('__ngf_gen__')) {
- angular.element(evt.target).remove();
- }
- } finally {
- isUpdating = false;
- }
- }
- }
- function bindAttrToFileInput(fileElem) {
- if (attr.ngMultiple) fileElem.attr('multiple', $parse(attr.ngMultiple)(scope));
- if (attr['accept']) fileElem.attr('accept', attr['accept']);
- if (attr.ngCapture) fileElem.attr('capture', $parse(attr.ngCapture)(scope));
- if (attr.ngDisabled) fileElem.attr('disabled', $parse(attr.ngDisabled)(scope));
- fileElem.bind('change', changeFn);
- }
- function createFileInput(evt) {
- if (elem.attr('disabled')) {
- return;
- }
- var fileElem = angular.element('<input type="file">');
- for (var i = 0; i < elem[0].attributes.length; i++) {
- var attribute = elem[0].attributes[i];
- fileElem.attr(attribute.name, attribute.value);
- }
- if (isInputTypeFile()) {
- elem.replaceWith(fileElem);
- elem = fileElem;
- } else {
- 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('__ngf_gen__', true);
- if (elem.__ngf_ref_elem__) elem.__ngf_ref_elem__.remove();
- elem.__ngf_ref_elem__ = fileElem;
- elem.parent()[0].insertBefore(fileElem[0], elem[0]);
- elem.css('overflow', 'hidden');
- }
- bindAttrToFileInput(fileElem);
- return fileElem;
- }
- function resetModel(evt) {
- updateModel($parse, $timeout, scope, ngModel, attr,
- attr.ngFileChange || attr.ngFileSelect, [], [], evt, true);
- }
- function clickHandler(evt) {
- var fileElem = createFileInput(evt);
- if (fileElem) {
- resetModel(evt);
- fileElem[0].click();
- }
- if (isInputTypeFile()) {
- elem.bind('click', clickHandler);
- evt.preventDefault()
- }
- }
- if (window.FileAPI && window.FileAPI.ngfFixIE) {
- window.FileAPI.ngfFixIE(elem, createFileInput, changeFn, resetModel);
- } else {
- elem.bind('click', clickHandler);
- }
- }
- angularFileUpload.directive('ngFileDrop', ['$parse', '$timeout', '$location', function ($parse, $timeout, $location) {
- return {
- restrict: 'AEC',
- require: '?ngModel',
- link: function (scope, elem, attr, ngModel) {
- linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $location);
- }
- }
- }]);
- angularFileUpload.directive('ngNoFileDrop', function () {
- return function (scope, elem) {
- 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 linkDrop(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) == true) {
- elem.css('display', 'none');
- }
- return;
- }
- var leaveTimeout = null;
- var stopPropagation = $parse(attr.stopPropagation);
- var dragOverDelay = 1;
- var accept = $parse(attr.ngAccept);
- var disabled = $parse(attr.ngDisabled);
- var actualDragOverClass;
- elem[0].addEventListener('dragover', function (evt) {
- if (disabled(scope)) return;
- evt.preventDefault();
- if (stopPropagation(scope)) 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) {
- if (disabled(scope)) return;
- evt.preventDefault();
- if (stopPropagation(scope)) evt.stopPropagation();
- }, false);
- elem[0].addEventListener('dragleave', function () {
- if (disabled(scope)) return;
- leaveTimeout = $timeout(function () {
- elem.removeClass(actualDragOverClass);
- actualDragOverClass = null;
- }, dragOverDelay || 1);
- }, false);
- elem[0].addEventListener('drop', function (evt) {
- if (disabled(scope)) return;
- evt.preventDefault();
- if (stopPropagation(scope)) evt.stopPropagation();
- elem.removeClass(actualDragOverClass);
- actualDragOverClass = null;
- extractFiles(evt, function (files, rejFiles) {
- updateModel($parse, $timeout, scope, ngModel, attr,
- attr.ngFileChange || attr.ngFileDrop, files, rejFiles, evt)
- }, $parse(attr.allowDir)(scope) != false, attr.multiple || $parse(attr.ngMultiple)(scope));
- }, false);
- function calculateDragOverClass(scope, attr, evt) {
- var accepted = true;
- var items = evt.dataTransfer.items;
- if (items != null) {
- for (i = 0; i < items.length && accepted; i++) {
- accepted = accepted
- && (items[i].kind == 'file' || items[i].kind == '')
- && isAccepted(scope, accept, items[i], evt);
- }
- }
- var clazz = $parse(attr.dragOverClass)(scope, {$event: evt});
- if (clazz) {
- if (clazz.delay) dragOverDelay = clazz.delay;
- if (clazz.accept) clazz = accepted ? 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 (isAccepted(scope, accept, file, evt)) {
- files.push(file);
- } else {
- rejFiles.push(file);
- }
- }
- if (items && items.length > 0 && $location.protocol() != 'file') {
- for (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 (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) {
- 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 (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 () {
- processing--;
- });
- }
- }
- }
- }
- }
- function dropAvailable() {
- var div = document.createElement('div');
- return ('draggable' in div) && ('ondrop' in div);
- }
- function updateModel($parse, $timeout, scope, ngModel, attr, fileChange, files, rejFiles, evt, noDelay) {
- function update() {
- if (ngModel) {
- $parse(attr.ngModel).assign(scope, files);
- $timeout(function () {
- ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? null : files);
- });
- }
- if (attr.ngModelRejected) {
- $parse(attr.ngModelRejected).assign(scope, rejFiles);
- }
- if (fileChange) {
- $parse(fileChange)(scope, {
- $files: files,
- $rejectedFiles: rejFiles,
- $event: evt
- });
- }
- }
- if (noDelay) {
- update();
- } else {
- $timeout(function () {
- update();
- });
- }
- }
- function isAccepted(scope, accept, file, evt) {
- var val = accept(scope, {$file: file, $event: evt});
- if (val == null) {
- return true;
- }
- if (angular.isString(val)) {
- var regexp = new RegExp(globStringToRegex(val), 'gi')
- val = (file.type != null && file.type.match(regexp)) ||
- (file.name != null && file.name.match(regexp));
- }
- return val;
- }
- 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 (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 (key in angularFileUpload) {
- if (angularFileUpload.hasOwnProperty(key)) {
- ngFileUpload[key] = angularFileUpload[key];
- }
- }
- })();
|