UNPKG

ng-file-upload-haikulearning

Version:

An AngularJS directive for file upload using HTML5 with FileAPI polyfill for unsupported browsers

340 lines (317 loc) 12 kB
(function () { ngFileUpload.directive('ngfDrop', ['$parse', '$timeout', '$location', 'Upload', '$http', '$q', function ($parse, $timeout, $location, Upload, $http, $q) { return { restrict: 'AEC', require: '?ngModel', link: function (scope, elem, attr, ngModel) { linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $location, Upload, $http, $q); } }; }]); ngFileUpload.directive('ngfNoFileDrop', function () { return function (scope, elem) { if (dropAvailable()) elem.css('display', 'none'); }; }); ngFileUpload.directive('ngfDropAvailable', ['$parse', '$timeout', 'Upload', function ($parse, $timeout, Upload) { return function (scope, elem, attr) { if (dropAvailable()) { var model = $parse(Upload.attrGetter('ngfDropAvailable', attr)); $timeout(function () { model(scope); if (model.assign) { model.assign(scope, true); } }); } }; }]); function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $location, upload, $http, $q) { var available = dropAvailable(); var attrGetter = function (name, scope, params) { return upload.attrGetter(name, attr, scope, params); }; if (attrGetter('dropAvailable')) { $timeout(function () { if (scope[attrGetter('dropAvailable')]) { scope[attrGetter('dropAvailable')].value = available; } else { scope[attrGetter('dropAvailable')] = available; } }); } if (!available) { if (attrGetter('ngfHideOnDropNotAvailable', scope) === true) { elem.css('display', 'none'); } return; } function isDisabled() { return elem.attr('disabled') || attrGetter('ngfDropDisabled', scope); } if (attrGetter('ngfSelect') == null) { upload.registerModelChangeValidator(ngModel, attr, scope); } var leaveTimeout = null; var stopPropagation = $parse(attrGetter('ngfStopPropagation')); var dragOverDelay = 1; var actualDragOverClass; elem[0].addEventListener('dragover', function (evt) { if (isDisabled() || !upload.shouldUpdateOn('drop', attr, 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 (!actualDragOverClass) { actualDragOverClass = 'C'; calculateDragOverClass(scope, attr, evt, function (clazz) { actualDragOverClass = clazz; elem.addClass(actualDragOverClass); attrGetter('ngfDrag', scope, {$isDragging: true, $class: actualDragOverClass, $event: evt}); }); } }, false); elem[0].addEventListener('dragenter', function (evt) { if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return; evt.preventDefault(); if (stopPropagation(scope)) evt.stopPropagation(); }, false); elem[0].addEventListener('dragleave', function (evt) { if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return; evt.preventDefault(); if (stopPropagation(scope)) evt.stopPropagation(); leaveTimeout = $timeout(function () { if (actualDragOverClass) elem.removeClass(actualDragOverClass); actualDragOverClass = null; attrGetter('ngfDrag', scope, {$isDragging: false, $event: evt}); }, dragOverDelay || 100); }, false); elem[0].addEventListener('drop', function (evt) { if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return; evt.preventDefault(); if (stopPropagation(scope)) evt.stopPropagation(); if (actualDragOverClass) elem.removeClass(actualDragOverClass); actualDragOverClass = null; var items = evt.dataTransfer.items; var html; try { html = evt.dataTransfer && evt.dataTransfer.getData && evt.dataTransfer.getData('text/html'); } catch (e) {/* Fix IE11 that throw error calling getData */ } extractFiles(items, evt.dataTransfer.files, attrGetter('ngfAllowDir', scope) !== false, attrGetter('multiple') || attrGetter('ngfMultiple', scope)).then(function (files) { if (files.length) { updateModel(files, evt); } else { extractFilesFromHtml('dropUrl', html).then(function (files) { updateModel(files, evt); }); } }); }, false); elem[0].addEventListener('paste', function (evt) { if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && attrGetter('ngfEnableFirefoxPaste', scope)) { evt.preventDefault(); } if (isDisabled() || !upload.shouldUpdateOn('paste', attr, scope)) return; var files = []; var clipboard = evt.clipboardData || evt.originalEvent.clipboardData; if (clipboard && clipboard.items) { for (var k = 0; k < clipboard.items.length; k++) { if (clipboard.items[k].type.indexOf('image') !== -1) { files.push(clipboard.items[k].getAsFile()); } } } if (files.length) { updateModel(files, evt); } else { extractFilesFromHtml('pasteUrl', clipboard).then(function (files) { updateModel(files, evt); }); } }, false); if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && attrGetter('ngfEnableFirefoxPaste', scope)) { elem.attr('contenteditable', true); elem.on('keypress', function (e) { if (!e.metaKey && !e.ctrlKey) { e.preventDefault(); } }); } function updateModel(files, evt) { upload.updateModel(ngModel, attr, scope, attrGetter('ngfChange') || attrGetter('ngfDrop'), files, evt); } function extractFilesFromHtml(updateOn, html) { if (!upload.shouldUpdateOn(updateOn, attr, scope) || !html) return upload.rejectPromise([]); var urls = []; html.replace(/<(img src|img [^>]* src) *=\"([^\"]*)\"/gi, function (m, n, src) { urls.push(src); }); var promises = [], files = []; if (urls.length) { angular.forEach(urls, function (url) { promises.push(upload.urlToBlob(url).then(function (blob) { files.push(blob); })); }); var defer = $q.defer(); $q.all(promises).then(function () { defer.resolve(files); }, function (e) { defer.reject(e); }); return defer.promise; } return upload.emptyPromise(); } function calculateDragOverClass(scope, attr, evt, callback) { var obj = attrGetter('ngfDragOverClass', scope, {$event: evt}), dClass = 'dragover'; if (angular.isString(obj)) { dClass = obj; } else if (obj) { if (obj.delay) dragOverDelay = obj.delay; if (obj.accept || obj.reject) { var items = evt.dataTransfer.items; if (items == null || !items.length) { dClass = obj.accept; } else { var pattern = obj.pattern || attrGetter('ngfPattern', scope, {$event: evt}); var len = items.length; while (len--) { if (!upload.validatePattern(items[len], pattern)) { dClass = obj.reject; break; } else { dClass = obj.accept; } } } } } callback(dClass); } function extractFiles(items, fileList, allowDir, multiple) { var maxFiles = upload.getValidationAttr(attr, scope, 'maxFiles') || Number.MAX_VALUE; var maxTotalSize = upload.getValidationAttr(attr, scope, 'maxTotalSize') || Number.MAX_VALUE; var includeDir = attrGetter('ngfIncludeDir', scope); var files = [], totalSize = 0; function traverseFileTree(entry, path) { var defer = $q.defer(); if (entry != null) { if (entry.isDirectory) { var promises = [upload.emptyPromise()]; if (includeDir) { var file = {type: 'directory'}; file.name = file.path = (path || '') + entry.name; files.push(file); } var dirReader = entry.createReader(); var entries = []; var readEntries = function () { dirReader.readEntries(function (results) { try { if (!results.length) { angular.forEach(entries.slice(0), function (e) { if (files.length <= maxFiles && totalSize <= maxTotalSize) { promises.push(traverseFileTree(e, (path ? path : '') + entry.name + '/')); } }); $q.all(promises).then(function () { defer.resolve(); }, function (e) { defer.reject(e); }); } else { entries = entries.concat(Array.prototype.slice.call(results || [], 0)); readEntries(); } } catch (e) { defer.reject(e); } }, function (e) { defer.reject(e); }); }; readEntries(); } else { entry.file(function (file) { try { file.path = (path ? path : '') + file.name; if (includeDir) { file = upload.rename(file, file.path); } files.push(file); totalSize += file.size; defer.resolve(); } catch (e) { defer.reject(e); } }, function (e) { defer.reject(e); }); } } return defer.promise; } var promises = [upload.emptyPromise()]; 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) { promises.push(traverseFileTree(entry)); } } else { var f = items[i].getAsFile(); if (f != null) { files.push(f); totalSize += f.size; } } if (files.length > maxFiles || totalSize > maxTotalSize || (!multiple && files.length > 0)) break; } } else { if (fileList != null) { for (var j = 0; j < fileList.length; j++) { var file = fileList.item(j); if (file.type || file.size > 0) { files.push(file); totalSize += file.size; } if (files.length > maxFiles || totalSize > maxTotalSize || (!multiple && files.length > 0)) break; } } } var defer = $q.defer(); $q.all(promises).then(function () { if (!multiple && !includeDir && files.length) { var i = 0; while (files[i] && files[i].type === 'directory') i++; defer.resolve([files[i]]); } else { defer.resolve(files); } }, function (e) { defer.reject(e); }); return defer.promise; } } function dropAvailable() { var div = document.createElement('div'); return ('draggable' in div) && ('ondrop' in div) && !/Edge\/12./i.test(navigator.userAgent); } })();