Browse Source

add angular-file-upload

Davide Alberani 9 years ago
parent
commit
20f1425cbe
2 changed files with 579 additions and 0 deletions
  1. 578 0
      static/js/angular-file-upload.js
  2. 1 0
      static/js/angular-file-upload.min.js

+ 578 - 0
static/js/angular-file-upload.js

@@ -0,0 +1,578 @@
+/**!
+ * 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];
+    }
+}
+
+})();

File diff suppressed because it is too large
+ 1 - 0
static/js/angular-file-upload.min.js


Some files were not shown because too many files changed in this diff