UNPKG

ng-file-upload-haikulearning

Version:

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

451 lines (410 loc) 14.7 kB
ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { var upload = UploadDataUrl; function globStringToRegex(str) { var regexp = '', excludes = []; if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { regexp = str.substring(1, str.length - 1); } else { var split = str.split(','); if (split.length > 1) { for (var i = 0; i < split.length; i++) { var r = globStringToRegex(split[i]); if (r.regexp) { regexp += regexp ? '|' : ''; regexp += '(' + r.regexp + ')'; } else { excludes = excludes.concat(r.excludes); } } } else { if (str.indexOf('!') === 0) { excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); } else { if (str.indexOf('.') === 0) { str = '*' + str; } regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); } } } return {regexp: regexp, excludes: excludes}; } upload.validatePattern = function (file, val) { if (!val) { return true; } var pattern = globStringToRegex(val), valid = true; if (pattern.regexp && pattern.regexp.length) { var regexp = new RegExp(pattern.regexp, 'i'); valid = (file.type != null && regexp.test(file.type)) || (file.name != null && regexp.test(file.name)); } var len = pattern.excludes.length; while (len--) { var exclude = new RegExp(pattern.excludes[len], 'i'); valid = valid && (file.type == null || exclude.test(file.type)) && (file.name == null || exclude.test(file.name)); } return valid; }; upload.ratioToFloat = function (val) { var r = val.toString(), xIndex = r.search(/[x:]/i); if (xIndex > -1) { r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); } else { r = parseFloat(r); } return r; }; upload.registerModelChangeValidator = function (ngModel, attr, scope) { if (ngModel) { ngModel.$formatters.push(function (files) { if (ngModel.$dirty) { if (files && !angular.isArray(files)) { files = [files]; } upload.validate(files, 0, ngModel, attr, scope).then(function () { upload.applyModelValidation(ngModel, files); }); } }); } }; function markModelAsDirty(ngModel, files) { if (files != null && !ngModel.$dirty) { if (ngModel.$setDirty) { ngModel.$setDirty(); } else { ngModel.$dirty = true; } } } upload.applyModelValidation = function (ngModel, files) { markModelAsDirty(ngModel, files); angular.forEach(ngModel.$ngfValidations, function (validation) { ngModel.$setValidity(validation.name, validation.valid); }); }; upload.getValidationAttr = function (attr, scope, name, validationName, file) { var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); var val = upload.attrGetter(dName, attr, scope, {$file: file}); if (val == null) { val = upload.attrGetter('ngfValidate', attr, scope, {$file: file}); if (val) { var split = (validationName || name).split('.'); val = val[split[0]]; if (split.length > 1) { val = val && val[split[1]]; } } } return val; }; upload.validate = function (files, prevLength, ngModel, attr, scope) { ngModel = ngModel || {}; ngModel.$ngfValidations = ngModel.$ngfValidations || []; angular.forEach(ngModel.$ngfValidations, function (v) { v.valid = true; }); var attrGetter = function (name, params) { return upload.attrGetter(name, attr, scope, params); }; if (files == null || files.length === 0) { return upload.emptyPromise(ngModel); } files = files.length === undefined ? [files] : files.slice(0); function validateSync(name, validationName, fn) { if (files) { var i = files.length, valid = null; while (i--) { var file = files[i]; if (file) { var val = upload.getValidationAttr(attr, scope, name, validationName, file); if (val != null) { if (!fn(file, val, i)) { file.$error = name; (file.$errorMessages = (file.$errorMessages || {}))[name] = true; file.$errorParam = val; files.splice(i, 1); valid = false; } } } } if (valid !== null) { ngModel.$ngfValidations.push({name: name, valid: valid}); } } } validateSync('maxFiles', null, function (file, val, i) { return prevLength + i < val; }); validateSync('pattern', null, upload.validatePattern); validateSync('minSize', 'size.min', function (file, val) { return file.size + 0.1 >= upload.translateScalars(val); }); validateSync('maxSize', 'size.max', function (file, val) { return file.size - 0.1 <= upload.translateScalars(val); }); var totalSize = 0; validateSync('maxTotalSize', null, function (file, val) { totalSize += file.size; if (totalSize > upload.translateScalars(val)) { files.splice(0, files.length); return false; } return true; }); validateSync('validateFn', null, function (file, r) { return r === true || r === null || r === ''; }); if (!files.length) { return upload.emptyPromise(ngModel, ngModel.$ngfValidations); } function validateAsync(name, validationName, type, asyncFn, fn) { function resolveResult(defer, file, val) { if (val != null) { asyncFn(file, val).then(function (d) { if (!fn(d, val)) { file.$error = name; (file.$errorMessages = (file.$errorMessages || {}))[name] = true; file.$errorParam = val; defer.reject(); } else { defer.resolve(); } }, function () { if (attrGetter('ngfValidateForce', {$file: file})) { file.$error = name; (file.$errorMessages = (file.$errorMessages || {}))[name] = true; file.$errorParam = val; defer.reject(); } else { defer.resolve(); } }); } else { defer.resolve(); } } var promises = [upload.emptyPromise()]; if (files) { files = files.length === undefined ? [files] : files; angular.forEach(files, function (file) { var defer = $q.defer(); promises.push(defer.promise); if (type && (file.type == null || file.type.search(type) !== 0)) { defer.resolve(); return; } if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { upload.imageDimensions(file).then(function (d) { resolveResult(defer, file, attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height})); }, function () { defer.reject(); }); } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { upload.mediaDuration(file).then(function (d) { resolveResult(defer, file, attrGetter('ngfDuration', {$file: file, $duration: d})); }, function () { defer.reject(); }); } else { resolveResult(defer, file, upload.getValidationAttr(attr, scope, name, validationName, file)); } }); return $q.all(promises).then(function () { ngModel.$ngfValidations.push({name: name, valid: true}); }, function () { ngModel.$ngfValidations.push({name: name, valid: false}); }); } } var deffer = $q.defer(); var promises = []; promises.push(upload.happyPromise(validateAsync('maxHeight', 'height.max', /image/, this.imageDimensions, function (d, val) { return d.height <= val; }))); promises.push(upload.happyPromise(validateAsync('minHeight', 'height.min', /image/, this.imageDimensions, function (d, val) { return d.height >= val; }))); promises.push(upload.happyPromise(validateAsync('maxWidth', 'width.max', /image/, this.imageDimensions, function (d, val) { return d.width <= val; }))); promises.push(upload.happyPromise(validateAsync('minWidth', 'width.min', /image/, this.imageDimensions, function (d, val) { return d.width >= val; }))); promises.push(upload.happyPromise(validateAsync('dimensions', null, /image/, function (file, val) { return upload.emptyPromise(val); }, function (r) { return r; }))); promises.push(upload.happyPromise(validateAsync('ratio', null, /image/, this.imageDimensions, function (d, val) { var split = val.toString().split(','), valid = false; for (var i = 0; i < split.length; i++) { if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.0001) { valid = true; } } return valid; }))); promises.push(upload.happyPromise(validateAsync('maxRatio', 'ratio.max', /image/, this.imageDimensions, function (d, val) { return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; }))); promises.push(upload.happyPromise(validateAsync('minRatio', 'ratio.min', /image/, this.imageDimensions, function (d, val) { return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; }))); promises.push(upload.happyPromise(validateAsync('maxDuration', 'duration.max', /audio|video/, this.mediaDuration, function (d, val) { return d <= upload.translateScalars(val); }))); promises.push(upload.happyPromise(validateAsync('minDuration', 'duration.min', /audio|video/, this.mediaDuration, function (d, val) { return d >= upload.translateScalars(val); }))); promises.push(upload.happyPromise(validateAsync('duration', null, /audio|video/, function (file, val) { return upload.emptyPromise(val); }, function (r) { return r; }))); promises.push(upload.happyPromise(validateAsync('validateAsyncFn', null, null, function (file, val) { return val; }, function (r) { return r === true || r === null || r === ''; }))); return $q.all(promises).then(function () { deffer.resolve(ngModel, ngModel.$ngfValidations); }); }; upload.imageDimensions = function (file) { if (file.$ngfWidth && file.$ngfHeight) { var d = $q.defer(); $timeout(function () { d.resolve({width: file.$ngfWidth, height: file.$ngfHeight}); }); return d.promise; } if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; var deferred = $q.defer(); $timeout(function () { if (file.type.indexOf('image') !== 0) { deferred.reject('not image'); return; } upload.dataUrl(file).then(function (dataUrl) { var img = angular.element('<img>').attr('src', dataUrl) .css('visibility', 'hidden').css('position', 'fixed') .css('max-width', 'none !important').css('max-height', 'none !important'); function success() { var width = img[0].clientWidth; var height = img[0].clientHeight; img.remove(); file.$ngfWidth = width; file.$ngfHeight = height; deferred.resolve({width: width, height: height}); } function error() { img.remove(); deferred.reject('load error'); } img.on('load', success); img.on('error', error); var count = 0; function checkLoadError() { $timeout(function () { if (img[0].parentNode) { if (img[0].clientWidth) { success(); } else if (count > 10) { error(); } else { checkLoadError(); } } }, 1000); } checkLoadError(); angular.element(document.getElementsByTagName('body')[0]).append(img); }, function () { deferred.reject('load error'); }); }); file.$ngfDimensionPromise = deferred.promise; file.$ngfDimensionPromise['finally'](function () { delete file.$ngfDimensionPromise; }); return file.$ngfDimensionPromise; }; upload.mediaDuration = function (file) { if (file.$ngfDuration) { var d = $q.defer(); $timeout(function () { d.resolve(file.$ngfDuration); }); return d.promise; } if (file.$ngfDurationPromise) return file.$ngfDurationPromise; var deferred = $q.defer(); $timeout(function () { if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { deferred.reject('not media'); return; } upload.dataUrl(file).then(function (dataUrl) { var el = angular.element(file.type.indexOf('audio') === 0 ? '<audio>' : '<video>') .attr('src', dataUrl).css('visibility', 'none').css('position', 'fixed'); function success() { var duration = el[0].duration; file.$ngfDuration = duration; el.remove(); deferred.resolve(duration); } function error() { el.remove(); deferred.reject('load error'); } el.on('loadedmetadata', success); el.on('error', error); var count = 0; function checkLoadError() { $timeout(function () { if (el[0].parentNode) { if (el[0].duration) { success(); } else if (count > 10) { error(); } else { checkLoadError(); } } }, 1000); } checkLoadError(); angular.element(document.body).append(el); }, function () { deferred.reject('load error'); }); }); file.$ngfDurationPromise = deferred.promise; file.$ngfDurationPromise['finally'](function () { delete file.$ngfDurationPromise; }); return file.$ngfDurationPromise; }; return upload; } ]);