From 9f5c4697bc5fa6b3f84bcfd3292b00053615e679 Mon Sep 17 00:00:00 2001 From: Danial Farid Date: Sat, 10 Oct 2015 16:53:36 -0400 Subject: [PATCH] Fixed validation on model change, added ngfDataUrl filter --- README.md | 1 - demo/src/main/webapp/index.html | 11 + demo/src/main/webapp/js/FileAPI.min.js | 2 +- demo/src/main/webapp/js/ng-file-upload-all.js | 120 +- .../main/webapp/js/ng-file-upload-all.min.js | 6 +- .../src/main/webapp/js/ng-file-upload-shim.js | 2 +- .../main/webapp/js/ng-file-upload-shim.min.js | 2 +- demo/src/main/webapp/js/ng-file-upload.js | 118 +- demo/src/main/webapp/js/ng-file-upload.min.js | 4 +- demo/src/main/webapp/js/ng-img-crop.css | 18 + demo/src/main/webapp/js/ng-img-crop.js | 1879 +++++++++++++++++ demo/src/main/webapp/js/upload.js | 2 +- dist/FileAPI.min.js | 2 +- dist/ng-file-upload-all.js | 120 +- dist/ng-file-upload-all.min.js | 6 +- dist/ng-file-upload-shim.js | 2 +- dist/ng-file-upload-shim.min.js | 2 +- dist/ng-file-upload.js | 118 +- dist/ng-file-upload.min.js | 4 +- nuget/Package.nuspec | 2 +- package.json | 2 +- src/data-url.js | 42 +- src/drop.js | 4 + src/model.js | 24 +- src/resize.js | 4 +- src/select.js | 2 + src/validate.js | 38 +- 27 files changed, 2267 insertions(+), 270 deletions(-) create mode 100755 demo/src/main/webapp/js/ng-img-crop.css create mode 100755 demo/src/main/webapp/js/ng-img-crop.js diff --git a/README.md b/README.md index ec91748e..8ea0e2b8 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,6 @@ At least one of the `ngf-select` or `ngf-drop` are mandatory for the plugin to l ngf-validate-force="boolean" // default false, if true file.$error will be set if the dimension or duration // values for validations cannot be calculated for example image load error or unsupported video by the browser. // by default it would assume the file is valid if the duration or dimension cannot be calculated by the browser. - ngf-validate-later="boolean" // default false, if true model will be set and change will be called before validation >Upload/Drop diff --git a/demo/src/main/webapp/index.html b/demo/src/main/webapp/index.html index 3457de1f..56c3a28c 100644 --- a/demo/src/main/webapp/index.html +++ b/demo/src/main/webapp/index.html @@ -39,6 +39,17 @@ + + + + diff --git a/demo/src/main/webapp/js/FileAPI.min.js b/demo/src/main/webapp/js/FileAPI.min.js index 317ad057..6b679ae9 100644 --- a/demo/src/main/webapp/js/FileAPI.min.js +++ b/demo/src/main/webapp/js/FileAPI.min.js @@ -1,4 +1,4 @@ -/*! 9.0.8 */ +/*! 9.0.9 */ /*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git * FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF. */ diff --git a/demo/src/main/webapp/js/ng-file-upload-all.js b/demo/src/main/webapp/js/ng-file-upload-all.js index 4e7e91b9..f17a82c7 100644 --- a/demo/src/main/webapp/js/ng-file-upload-all.js +++ b/demo/src/main/webapp/js/ng-file-upload-all.js @@ -3,7 +3,7 @@ * progress, resize, thumbnail, preview, validation and CORS * FileAPI Flash shim for old browsers not supporting FormData * @author Danial - * @version 9.0.8 + * @version 9.0.9 */ (function () { @@ -427,7 +427,7 @@ if (!window.FileReader) { * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort, * progress, resize, thumbnail, preview, validation and CORS * @author Danial - * @version 9.0.8 + * @version 9.0.9 */ if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) { @@ -448,7 +448,7 @@ if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) { var ngFileUpload = angular.module('ngFileUpload', []); -ngFileUpload.version = '9.0.8'; +ngFileUpload.version = '9.0.9'; ngFileUpload.service('UploadBase', ['$http', '$q', '$timeout', function ($http, $q, $timeout) { var upload = this; @@ -801,16 +801,6 @@ ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', 'UploadResize' return true; }; - function markModelAsDirty(ngModel, files) { - if (files != null && !ngModel.$dirty) { - if (ngModel.$setDirty) { - ngModel.$setDirty(); - } else { - ngModel.$dirty = true; - } - } - } - function resize(files, attr, scope, callback) { var param = upload.attrGetter('ngfResize', attr, scope); if (!param || !upload.isResizeSupported()) return callback(); @@ -877,15 +867,9 @@ ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', 'UploadResize' var file = files && files.length ? files[0] : null; if (ngModel) { - markModelAsDirty(ngModel, files); - - angular.forEach(ngModel.$ngfValidations, function (validation) { - ngModel.$setValidity(validation.name, validation.valid); - }); - - if (ngModel) { - ngModel.$setViewValue(isSingleModel ? file : files); - } + upload.applyModelValidation(ngModel, files); + ngModel.$ngfModelChange = true; + ngModel.$setViewValue(isSingleModel ? file : files); } if (fileChange) { @@ -919,7 +903,7 @@ ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', 'UploadResize' attr.$$ngfPrevFiles = files; - if (upload.validate(newFiles, ngModel, attr, scope, upload.attrGetter('ngfValidateLater', attr), function () { + if (upload.validate(newFiles, ngModel, attr, scope, function () { if (noDelay) { update(files, [], newFiles, dupFiles, isSingleModel); } else { @@ -1004,6 +988,8 @@ ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', } } + upload.registerModelChangeValidator(ngModel, attr, scope); + var unwatches = []; unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () { fileElem.attr('multiple', attrGetter('ngfMultiple', scope)); @@ -1385,26 +1371,28 @@ ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', }; }]); - //ngFileUpload.config(['$compileProvider', function ($compileProvider) { - // if ($compileProvider.imgSrcSanitizationWhitelist) $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|local|file|data|blob):/); - // if ($compileProvider.aHrefSanitizationWhitelist) $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|local|file|data|blob):/); - //}]); - // - //ngFileUpload.filter('$ngfDataUrl', ['UploadDataUrl', '$sce', function (UploadDataUrl, $sce) { - // return function (file, disallowObjectUrl) { - // if (angular.isString(file)) { - // return $sce.trustAsResourceUrl(file); - // } - // if (file && !file.$ngfDataUrl) { - // if (file.$ngfDataUrl === undefined && angular.isObject(file)) { - // file.$ngfDataUrl = null; - // UploadDataUrl.$ngfDataUrl(file, disallowObjectUrl); - // } - // return ''; - // } - // return (file && file.$ngfDataUrl ? $sce.trustAsResourceUrl(file.$ngfDataUrl) : file) || ''; - // }; - //}]); + ngFileUpload.config(['$compileProvider', function ($compileProvider) { + if ($compileProvider.imgSrcSanitizationWhitelist) $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|local|file|data|blob):/); + if ($compileProvider.aHrefSanitizationWhitelist) $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|local|file|data|blob):/); + }]); + + ngFileUpload.filter('ngfDataUrl', ['UploadDataUrl', '$sce', function (UploadDataUrl, $sce) { + return function (file, disallowObjectUrl, trustedUrl) { + if (angular.isString(file)) { + return $sce.trustAsResourceUrl(file); + } + var src = file && ((disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl); + if (file && !src) { + if (!file.$ngfDataUrlFilterInProgress && angular.isObject(file)) { + file.$ngfDataUrlFilterInProgress = true; + UploadDataUrl.dataUrl(file, disallowObjectUrl); + } + return ''; + } + if (file) delete file.$ngfDataUrlFilterInProgress; + return (file && src ? (trustedUrl ? $sce.trustAsResourceUrl(src) : src) : file) || ''; + }; + }]); })(); @@ -1463,7 +1451,38 @@ ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', funct return valid; }; - upload.validate = function (files, ngModel, attr, scope, later, callback) { + upload.registerModelChangeValidator = function (ngModel, attr, scope) { + if (ngModel) { + ngModel.$formatters.push(function (files) { + if (!ngModel.$ngfModelChange) { + upload.validate(files, ngModel, attr, scope, function () { + upload.applyModelValidation(ngModel, files); + }); + } else { + ngModel.$ngfModelChange = false; + } + }); + } + }; + + 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.validate = function (files, ngModel, attr, scope, callback) { ngModel = ngModel || {}; ngModel.$ngfValidations = ngModel.$ngfValidations || []; @@ -1475,11 +1494,6 @@ ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', funct return upload.attrGetter(name, attr, scope, params); }; - if (later) { - callback.call(ngModel); - return; - } - if (files == null || files.length === 0) { callback.call(ngModel); return; @@ -1833,7 +1847,7 @@ ngFileUpload.service('UploadResize', ['UploadValidate', '$q', '$timeout', functi return deferred.promise; }; - var dataURLtoBlob = function (dataurl) { + upload.dataUrltoBlob = function (dataurl) { var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { @@ -1858,7 +1872,7 @@ ngFileUpload.service('UploadResize', ['UploadValidate', '$q', '$timeout', functi upload.dataUrl(file, true).then(function (url) { resize(url, width, height, quality, file.type).then(function (dataUrl) { - var blob = dataURLtoBlob(dataUrl); + var blob = upload.dataUrltoBlob(dataUrl); blob.name = file.name; deferred.resolve(blob); }, function () { @@ -1932,6 +1946,10 @@ ngFileUpload.service('UploadResize', ['UploadValidate', '$q', '$timeout', functi 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; diff --git a/demo/src/main/webapp/js/ng-file-upload-all.min.js b/demo/src/main/webapp/js/ng-file-upload-all.min.js index 571f0eb9..78304c13 100644 --- a/demo/src/main/webapp/js/ng-file-upload-all.min.js +++ b/demo/src/main/webapp/js/ng-file-upload-all.min.js @@ -1,3 +1,3 @@ -/*! 9.0.8 */ -!function(){function a(a,b){window.XMLHttpRequest.prototype[a]=b(window.XMLHttpRequest.prototype[a])}function b(a,b,c){try{Object.defineProperty(a,b,{get:c})}catch(d){}}if(window.FileAPI||(window.FileAPI={}),!window.XMLHttpRequest)throw"AJAX is not supported. XMLHttpRequest is not defined.";if(FileAPI.shouldLoad=!window.FormData||FileAPI.forceLoad,FileAPI.shouldLoad){var c=function(a){if(!a.__listeners){a.upload||(a.upload={}),a.__listeners=[];var b=a.upload.addEventListener;a.upload.addEventListener=function(c,d){a.__listeners[c]=d,b&&b.apply(this,arguments)}}};a("open",function(a){return function(b,d,e){c(this),this.__url=d;try{a.apply(this,[b,d,e])}catch(f){f.message.indexOf("Access is denied")>-1&&(this.__origError=f,a.apply(this,[b,"_fix_for_ie_crossdomain__",e]))}}}),a("getResponseHeader",function(a){return function(b){return this.__fileApiXHR&&this.__fileApiXHR.getResponseHeader?this.__fileApiXHR.getResponseHeader(b):null==a?null:a.apply(this,[b])}}),a("getAllResponseHeaders",function(a){return function(){return this.__fileApiXHR&&this.__fileApiXHR.getAllResponseHeaders?this.__fileApiXHR.getAllResponseHeaders():null==a?null:a.apply(this)}}),a("abort",function(a){return function(){return this.__fileApiXHR&&this.__fileApiXHR.abort?this.__fileApiXHR.abort():null==a?null:a.apply(this)}}),a("setRequestHeader",function(a){return function(b,d){if("__setXHR_"===b){c(this);var e=d(this);e instanceof Function&&e(this)}else this.__requestHeaders=this.__requestHeaders||{},this.__requestHeaders[b]=d,a.apply(this,arguments)}}),a("send",function(a){return function(){var c=this;if(arguments[0]&&arguments[0].__isFileAPIShim){var d=arguments[0],e={url:c.__url,jsonp:!1,cache:!0,complete:function(a,d){a&&angular.isString(a)&&-1!==a.indexOf("#2174")&&(a=null),c.__completed=!0,!a&&c.__listeners.load&&c.__listeners.load({type:"load",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),!a&&c.__listeners.loadend&&c.__listeners.loadend({type:"loadend",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),"abort"===a&&c.__listeners.abort&&c.__listeners.abort({type:"abort",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),void 0!==d.status&&b(c,"status",function(){return 0===d.status&&a&&"abort"!==a?500:d.status}),void 0!==d.statusText&&b(c,"statusText",function(){return d.statusText}),b(c,"readyState",function(){return 4}),void 0!==d.response&&b(c,"response",function(){return d.response});var e=d.responseText||(a&&0===d.status&&"abort"!==a?a:void 0);b(c,"responseText",function(){return e}),b(c,"response",function(){return e}),a&&b(c,"err",function(){return a}),c.__fileApiXHR=d,c.onreadystatechange&&c.onreadystatechange(),c.onload&&c.onload()},progress:function(a){if(a.target=c,c.__listeners.progress&&c.__listeners.progress(a),c.__total=a.total,c.__loaded=a.loaded,a.total===a.loaded){var b=this;setTimeout(function(){c.__completed||(c.getAllResponseHeaders=function(){},b.complete(null,{status:204,statusText:"No Content"}))},FileAPI.noContentTimeout||1e4)}},headers:c.__requestHeaders};e.data={},e.files={};for(var f=0;f-1){e=h.substring(0,g+1);break}null==FileAPI.staticPath&&(FileAPI.staticPath=e),i.setAttribute("src",d||e+"FileAPI.min.js"),document.getElementsByTagName("head")[0].appendChild(i)}FileAPI.ngfFixIE=function(d,e,f){if(!b())throw'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';var g=function(){d.attr("disabled")?e&&e.removeClass("js-fileapi-wrapper"):(e.attr("__ngf_flash_")||(e.unbind("change"),e.unbind("click"),e.bind("change",function(a){h.apply(this,[a]),f.apply(this,[a])}),e.attr("__ngf_flash_","true")),e.addClass("js-fileapi-wrapper"),a(d)||e.css("position","absolute").css("top",c(d[0]).top+"px").css("left",c(d[0]).left+"px").css("width",d[0].offsetWidth+"px").css("height",d[0].offsetHeight+"px").css("filter","alpha(opacity=0)").css("display",d.css("display")).css("overflow","hidden").css("z-index","900000").css("visibility","visible"))};d.bind("mouseenter",g);var h=function(a){for(var b=FileAPI.getFiles(a),c=0;c=b.size&&(a._finished=!0,a._end=b.size);var d=b.slice(a._start,a._end||b.size);return d.name=b.name,d.ngfName=b.ngfName,a._chunkSize&&(c.append("_chunkSize",a._end-a._start),c.append("_chunkNumber",Math.floor(a._start/a._chunkSize)),c.append("_totalSize",a._file.size)),d}return b}function g(d,e,f){if(void 0!==e)if(angular.isDate(e)&&(e=e.toISOString()),angular.isString(e))d.append(f,e);else if(b(e)){var h=c(e,d),i=f.split(",");i[1]&&(h.ngfName=i[1].replace(/^\s+|\s+$/g,""),f=i[0]),a._fileKey=a._fileKey||f,d.append(f,h,h.ngfName||h.name)}else if(angular.isObject(e)){if(e.$$ngfCircularDetection)throw"ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: "+f;e.$$ngfCircularDetection=!0;try{for(var j in e)if(e.hasOwnProperty(j)&&"$$ngfCircularDetection"!==j){var k=null==a.objectKey?"[i]":a.objectKey;e.length&&parseInt(j)>-1&&(k=null==a.arrayKey?k:a.arrayKey),g(d,e[j],f+k.replace(/[ik]/g,j))}}finally{delete e.$$ngfCircularDetection}}else d.append(f,e)}function h(){a._chunkSize=e.translateScalars(a.resumeChunkSize),a._chunkSize=a._chunkSize?parseInt(a._chunkSize.toString()):null,a.headers=a.headers||{},a.headers["Content-Type"]=void 0,a.transformRequest=a.transformRequest?angular.isArray(a.transformRequest)?a.transformRequest:[a.transformRequest]:[],a.transformRequest.push(function(b){var c,d=new FormData;b=b||a.fields||{},a.file&&(b.file=a.file);for(c in b)if(b.hasOwnProperty(c)){var e=b[c];a.formDataAppender?a.formDataAppender(d,c,e):g(d,e,c)}return d})}return a._isDigested||(a._isDigested=!0,h()),d(a)},this.http=function(b){return b.transformRequest=b.transformRequest||function(b){return window.ArrayBuffer&&b instanceof window.ArrayBuffer||b instanceof Blob?b:a.defaults.transformRequest[0].apply(this,arguments)},b._chunkSize=e.translateScalars(b.resumeChunkSize),b._chunkSize=b._chunkSize?parseInt(b._chunkSize.toString()):null,d(b)},this.translateScalars=function(a){if(angular.isString(a)){if(a.search(/kb/i)===a.length-2)return parseFloat(1e3*a.substring(0,a.length-2));if(a.search(/mb/i)===a.length-2)return parseFloat(1e6*a.substring(0,a.length-2));if(a.search(/gb/i)===a.length-2)return parseFloat(1e9*a.substring(0,a.length-2));if(a.search(/b/i)===a.length-1)return parseFloat(a.substring(0,a.length-1));if(a.search(/s/i)===a.length-1)return parseFloat(a.substring(0,a.length-1));if(a.search(/m/i)===a.length-1)return parseFloat(60*a.substring(0,a.length-1));if(a.search(/h/i)===a.length-1)return parseFloat(3600*a.substring(0,a.length-1))}return a},this.setDefaults=function(a){this.defaults=a||{}},this.defaults={},this.version=ngFileUpload.version}]),ngFileUpload.service("Upload",["$parse","$timeout","$compile","UploadResize",function(a,b,c,d){function e(a,b){null==b||a.$dirty||(a.$setDirty?a.$setDirty():a.$dirty=!0)}function f(a,b,c,d){var e=h.attrGetter("ngfResize",b,c);if(!e||!h.isResizeSupported())return d();for(var f=a.length,g=function(){f--,0===f&&d()},i=function(b){return function(c){a.splice(b,1,c),g()}},j=function(a){return function(b){g(),a.$error="resize",a.$errorParam=(b?(b.message?b.message:b)+": ":"")+(a&&a.name)}},k=0;kk;k++)if(a[j].name===b[k].name){e.push(a[j]);break}k===i&&(b.push(a[j]),g=!0)}a=b}else a=b.concat(a||[])}return{files:a,dupFiles:e,keep:f}}var h=d;return h.getAttrWithDefaults=function(a,b){if(null!=a[b])return a[b];var c=h.defaults[b];return null==c?c:angular.isString(c)?c:JSON.stringify(c)},h.attrGetter=function(b,c,d,e){var f=this.getAttrWithDefaults(c,b);if(!d)return f;try{return e?a(f)(d,e):a(f)(d)}catch(g){if(b.search(/min|max|pattern/i))return f;throw g}},h.shouldUpdateOn=function(a,b,c){var d=h.attrGetter("ngModelOptions",b,c);return d&&d.updateOn?d.updateOn.split(" ").indexOf(a)>-1:!0},h.updateModel=function(c,d,i,j,k,l,m){function n(f,g,k,m,n){var o=f&&f.length?f[0]:null;c&&(e(c,f),angular.forEach(c.$ngfValidations,function(a){c.$setValidity(a.name,a.valid)}),c&&c.$setViewValue(n?o:f)),j&&a(j)(i,{$files:f,$file:o,$newFiles:k,$duplicateFiles:m,$invalidFiles:g,$event:l});var p=h.attrGetter("ngfModelInvalid",d);p&&b(function(){a(p).assign(i,g)}),b(function(){})}var o=k,p=(c&&c.$modelValue||d.$$ngfPrevFiles||[]).slice(0),q=g(k,p,d,i);k=q.files;var r=q.dupFiles,s=!h.attrGetter("ngfMultiple",d,i)&&!h.attrGetter("multiple",d)&&!q.keep;d.$$ngfPrevFiles=k,h.validate(o,c,d,i,h.attrGetter("ngfValidateLater",d),function(){if(m)n(k,[],o,r,s);else{var a=h.attrGetter("ngModelOptions",d,i);if(!a||!a.allowInvalid){var c=[],e=[];angular.forEach(k,function(a){a.$error?e.push(a):c.push(a)}),k=c}f(k,d,i,function(){b(function(){n(k,e,o,r,s)},a&&a.debounce?a.debounce.change||a.debounce:0)})}});for(var t=p.length;t--;){var u=p[t];window.URL&&u.blobUrl&&(URL.revokeObjectURL(u.blobUrl),delete u.blobUrl)}},h}]),ngFileUpload.directive("ngfSelect",["$parse","$timeout","$compile","Upload",function(a,b,c,d){function e(a){var b=a.match(/Android[^\d]*(\d+)\.(\d+)/);if(b&&b.length>2){var c=d.defaults.androidFixMinorVersion||4;return parseInt(b[1])<4||parseInt(b[1])===c&&parseInt(b[2])');return n(a),a.css("visibility","hidden").css("position","absolute").css("overflow","hidden").css("width","0px").css("height","0px").css("border","none").css("margin","0px").css("padding","0px").attr("tabindex","-1"),g.push({el:b,ref:a}),document.body.appendChild(a[0]),a}function p(c){if(b.attr("disabled")||t("ngfSelectDisabled",a))return!1;var d=q(c);return null!=d?d:(r(c),e(navigator.userAgent)?setTimeout(function(){w[0].click()},0):w[0].click(),!1)}function q(a){var b=a.changedTouches||a.originalEvent&&a.originalEvent.changedTouches;if("touchstart"===a.type)return v=b?b[0].clientY:0,!0;if(a.stopPropagation(),a.preventDefault(),"touchend"===a.type){var c=b?b[0].clientY:0;if(Math.abs(c-v)>20)return!1}}function r(b){j.shouldUpdateOn("click",c,a)&&w.val()&&(w.val(null),j.updateModel(d,c,a,l(),null,b,!0))}function s(a){if(w&&!w.attr("__ngf_ie10_Fix_")){if(!w[0].parentNode)return void(w=null);a.preventDefault(),a.stopPropagation(),w.unbind("click");var b=w.clone();return w.replaceWith(b),w=b,w.attr("__ngf_ie10_Fix_","true"),w.bind("change",m),w.bind("click",s),w[0].click(),!1}w.removeAttr("__ngf_ie10_Fix_")}var t=function(a,b){return j.attrGetter(a,c,b)},u=[];u.push(a.$watch(t("ngfMultiple"),function(){w.attr("multiple",t("ngfMultiple",a))})),u.push(a.$watch(t("ngfCapture"),function(){w.attr("capture",t("ngfCapture",a))})),c.$observe("accept",function(){w.attr("accept",t("accept"))}),u.push(function(){c.$$observers&&delete c.$$observers.accept});var v=0,w=b;k()||(w=o()),w.bind("change",m),k()?b.bind("click",r):b.bind("click touchstart touchend",p),-1!==navigator.appVersion.indexOf("MSIE 10")&&w.bind("click",s),d&&d.$formatters.push(function(a){return(null==a||0===a.length)&&w.val()&&w.val(null),a}),a.$on("$destroy",function(){k()||w.remove(),angular.forEach(u,function(a){a()})}),h(function(){for(var a=0;a.ngf-hide{display:none !important}");document.getElementsByTagName("head")[0].appendChild(c[0]),ngFileUpload.directive("ngfSrc",["Upload","$timeout",function(a,c){return{restrict:"AE",link:function(d,e,f){b(a,c,d,e,f,"ngfSrc",a.attrGetter("ngfResize",f,d),!1)}}}]),ngFileUpload.directive("ngfBackground",["Upload","$timeout",function(a,c){return{restrict:"AE",link:function(d,e,f){b(a,c,d,e,f,"ngfBackground",a.attrGetter("ngfResize",f,d),!0)}}}]),ngFileUpload.directive("ngfThumbnail",["Upload","$timeout",function(a,c){return{restrict:"AE",link:function(d,e,f){var g=a.attrGetter("ngfSize",f,d);b(a,c,d,e,f,"ngfThumbnail",g,a.attrGetter("ngfAsBackground",f,d))}}}])}(),ngFileUpload.service("UploadValidate",["UploadDataUrl","$q","$timeout",function(a,b,c){function d(a){var b="",c=[];if(a.length>2&&"/"===a[0]&&"/"===a[a.length-1])b=a.substring(1,a.length-1);else{var e=a.split(",");if(e.length>1)for(var f=0;f|:\\-]","g"),"\\$&")+"$",b=b.replace(/\\\*/g,".*").replace(/\\\?/g,"."))}return{regexp:b,excludes:c}}var e=a;return e.validatePattern=function(a,b){if(!b)return!0;var c=d(b),e=!0;if(c.regexp&&c.regexp.length){var f=new RegExp(c.regexp,"i");e=null!=a.type&&f.test(a.type)||null!=a.name&&f.test(a.name)}for(var g=c.excludes.length;g--;){var h=new RegExp(c.excludes[g],"i");e=e&&(null==a.type||h.test(a.type))&&(null==a.name||h.test(a.name))}return e},e.validate=function(a,b,c,d,f,g){function h(c,d,e){if(a){for(var f="ngf"+c[0].toUpperCase()+c.substr(1),g=a.length,h=null;g--;){var i=a[g],k=j(f,{$file:i});null==k&&(k=d(j("ngfValidate")||{}),h=null==h?!0:h),null!=k&&(e(i,k)||(i.$error=c,i.$errorParam=k,a.splice(g,1),h=!1))}null!==h&&b.$ngfValidations.push({name:c,valid:h})}}function i(c,d,e,f,h){if(a){var i=0,l=!1,m="ngf"+c[0].toUpperCase()+c.substr(1);a=void 0===a.length?[a]:a,angular.forEach(a,function(a){if(0!==a.type.search(e))return!0;var n=j(m,{$file:a})||d(j("ngfValidate",{$file:a})||{});n&&(k++,i++,f(a,n).then(function(b){h(b,n)||(a.$error=c,a.$errorParam=n,l=!0)},function(){j("ngfValidateForce",{$file:a})&&(a.$error=c,a.$errorParam=n,l=!0)})["finally"](function(){k--,i--,i||b.$ngfValidations.push({name:c,valid:!l}),k||g.call(b,b.$ngfValidations)}))})}}b=b||{},b.$ngfValidations=b.$ngfValidations||[],angular.forEach(b.$ngfValidations,function(a){a.valid=!0});var j=function(a,b){return e.attrGetter(a,c,d,b)};if(f)return void g.call(b);if(null==a||0===a.length)return void g.call(b);if(a=void 0===a.length?[a]:a.slice(0),h("pattern",function(a){return a.pattern},e.validatePattern),h("minSize",function(a){return a.size&&a.size.min},function(a,b){return a.size>=e.translateScalars(b)}),h("maxSize",function(a){return a.size&&a.size.max},function(a,b){return a.size<=e.translateScalars(b)}),h("validateFn",function(){return null},function(a,b){return b===!0||null===b||""===b}),!a.length)return void g.call(b,b.$ngfValidations);var k=0;i("maxHeight",function(a){return a.height&&a.height.max},/image/,this.imageDimensions,function(a,b){return a.height<=b}),i("minHeight",function(a){return a.height&&a.height.min},/image/,this.imageDimensions,function(a,b){return a.height>=b}),i("maxWidth",function(a){return a.width&&a.width.max},/image/,this.imageDimensions,function(a,b){return a.width<=b}),i("minWidth",function(a){return a.width&&a.width.min},/image/,this.imageDimensions,function(a,b){return a.width>=b}),i("ratio",function(a){return a.ratio},/image/,this.imageDimensions,function(a,b){for(var c=b.toString().split(","),d=!1,e=0;e-1?parseFloat(f.substring(0,g))/parseFloat(f.substring(g+1)):parseFloat(f),Math.abs(a.width/a.height-f)<1e-4&&(d=!0)}return d}),i("maxDuration",function(a){return a.duration&&a.duration.max},/audio|video/,this.mediaDuration,function(a,b){return a<=e.translateScalars(b)}),i("minDuration",function(a){return a.duration&&a.duration.min},/audio|video/,this.mediaDuration,function(a,b){return a>=e.translateScalars(b)}),i("validateAsyncFn",function(){return null},/./,function(a,b){return b},function(a){return a===!0||null===a||""===a}),k||g.call(b,b.$ngfValidations)},e.imageDimensions=function(a){if(a.$ngfWidth&&a.$ngfHeight){var d=b.defer();return c(function(){d.resolve({width:a.$ngfWidth,height:a.$ngfHeight})}),d.promise}if(a.$ngfDimensionPromise)return a.$ngfDimensionPromise;var f=b.defer();return c(function(){return 0!==a.type.indexOf("image")?void f.reject("not image"):void e.dataUrl(a).then(function(b){function d(){var b=h[0].clientWidth,c=h[0].clientHeight;h.remove(),a.$ngfWidth=b,a.$ngfHeight=c,f.resolve({width:b,height:c})}function e(){h.remove(),f.reject("load error")}function g(){c(function(){h[0].parentNode&&(h[0].clientWidth?d():i>10?e():g())},1e3)}var h=angular.element("").attr("src",b).css("visibility","hidden").css("position","fixed");h.on("load",d),h.on("error",e);var i=0;g(),angular.element(document.getElementsByTagName("body")[0]).append(h)},function(){f.reject("load error")})}),a.$ngfDimensionPromise=f.promise,a.$ngfDimensionPromise["finally"](function(){delete a.$ngfDimensionPromise}),a.$ngfDimensionPromise},e.mediaDuration=function(a){if(a.$ngfDuration){var d=b.defer();return c(function(){d.resolve(a.$ngfDuration)}),d.promise}if(a.$ngfDurationPromise)return a.$ngfDurationPromise;var f=b.defer();return c(function(){return 0!==a.type.indexOf("audio")&&0!==a.type.indexOf("video")?void f.reject("not media"):void e.dataUrl(a).then(function(b){function d(){var b=h[0].duration;a.$ngfDuration=b,h.remove(),f.resolve(b)}function e(){h.remove(),f.reject("load error")}function g(){c(function(){h[0].parentNode&&(h[0].duration?d():i>10?e():g())},1e3)}var h=angular.element(0===a.type.indexOf("audio")?"