{"version":3,"sources":["angular-base64-upload.js"],"names":["window","undefined","_arrayBufferToBase64","buffer","binary","bytes","Uint8Array","len","byteLength","i","String","fromCharCode","btoa","angular","module","directive","$window","$q","isolateScope","onChange","onAfterValidate","parser","FILE_READER_EVENTS","forEach","e","restrict","require","scope","link","elem","attrs","ngModel","_maxnum","val","maxnum","multiple","valid","length","parseInt","$setValidity","_minnum","minnum","_maxsize","maxsize","max","parseFloat","file","filesize","_minsize","min","minsize","_accept","regExp","exp","fileExt","accept","trim","replace","RegExp","filename","split","pop","test","filetype","_setViewValue","newVal","fileObjects","$setViewValue","_attachHandlerForEvent","eventName","handler","fReader","fileObject","rawFiles","_readerOnLoad","promise","target","result","exceedsMaxSize","size","doNotParseIfOversize","base64","when","then","fileObj","push","deferredObj","resolve","onload","_attachEventHandlers","_readFiles","promises","defer","all","reader","FileReader","type","name","readAsArrayBuffer","_onChange","_onAfterValidate","$isEmpty","isArray","_clearInput","value","$watch","$viewValue","$dirty","on","copy","files","allowSameFile"],"mappings":"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","file":"angular-base64-upload.min.js","sourcesContent":["(function(window, undefined) {\n\n  'use strict';\n\n  /* istanbul ignore next */\n  //http://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string\n  window._arrayBufferToBase64 = function(buffer) {\n    var binary = '';\n    var bytes = new Uint8Array(buffer);\n    var len = bytes.byteLength;\n\n    for (var i = 0; i < len; i += 1) {\n      binary += String.fromCharCode(bytes[i]);\n    }\n    return window.btoa(binary);\n  };\n\n\n  var mod = window.angular.module('naif.base64', []);\n\n  mod.directive('baseSixtyFourInput', [\n    '$window',\n    '$q',\n    function($window, $q) {\n\n      var isolateScope = {\n        onChange: '&',\n        onAfterValidate: '&',\n        parser: '&'\n      };\n\n      var FILE_READER_EVENTS = ['onabort', 'onerror', 'onloadstart', 'onloadend', 'onprogress', 'onload'];\n\n      FILE_READER_EVENTS.forEach(function(e) {\n        isolateScope[e] = '&';\n      });\n\n      return {\n        restrict: 'A',\n        require: 'ngModel',\n        scope: isolateScope,\n        link: function(scope, elem, attrs, ngModel) {\n\n          var rawFiles = [];\n          var fileObjects = [];\n\n          /* istanbul ignore if */\n          if (!ngModel) {\n            return;\n          }\n\n          // VALIDATIONS =========================================================\n\n          function _maxnum(val) {\n            if (attrs.maxnum && attrs.multiple && val) {\n              var valid = val.length <= parseInt(attrs.maxnum);\n              ngModel.$setValidity('maxnum', valid);\n            }\n            return val;\n          }\n\n          function _minnum(val) {\n            if (attrs.minnum && attrs.multiple && val) {\n              var valid = val.length >= parseInt(attrs.minnum);\n              ngModel.$setValidity('minnum', valid);\n            }\n            return val;\n          }\n\n          function _maxsize(val) {\n            var valid = true;\n\n            if (attrs.maxsize && val) {\n              var max = parseFloat(attrs.maxsize) * 1000;\n\n              if (attrs.multiple) {\n                for (var i = 0; i < val.length; i++) {\n                  var file = val[i];\n                  if (file.filesize > max) {\n                    valid = false;\n                    break;\n                  }\n                }\n              } else {\n                valid = val.filesize <= max;\n              }\n              ngModel.$setValidity('maxsize', valid);\n            }\n\n            return val;\n          }\n\n          function _minsize(val) {\n            var valid = true;\n            var min = parseFloat(attrs.minsize) * 1000;\n\n            if (attrs.minsize && val) {\n              if (attrs.multiple) {\n                for (var i = 0; i < val.length; i++) {\n                  var file = val[i];\n                  if (file.filesize < min) {\n                    valid = false;\n                    break;\n                  }\n                }\n              } else {\n                valid = val.filesize >= min;\n              }\n              ngModel.$setValidity('minsize', valid);\n            }\n\n            return val;\n          }\n\n          function _accept(val) {\n            var valid = true;\n            var regExp, exp, fileExt;\n            if (attrs.accept) {\n              exp = attrs.accept.trim().replace(/[,\\s]+/gi, \"|\").replace(/\\./g, \"\\\\.\").replace(/\\/\\*/g, \"/.*\");\n              regExp = new RegExp(exp);\n            }\n\n            if (attrs.accept && val) {\n              if (attrs.multiple) {\n                for (var i = 0; i < val.length; i++) {\n                  var file = val[i];\n                  fileExt = \".\" + file.filename.split('.').pop();\n                  valid = regExp.test(file.filetype) || regExp.test(fileExt);\n\n                  if (!valid) {\n                    break;\n                  }\n                }\n              } else {\n                fileExt = \".\" + val.filename.split('.').pop();\n                valid = regExp.test(val.filetype) || regExp.test(fileExt);\n              }\n              ngModel.$setValidity('accept', valid);\n            }\n\n            return val;\n          }\n\n          //end validations ===============\n\n          function _setViewValue() {\n            var newVal = attrs.multiple ? fileObjects : fileObjects[0];\n            ngModel.$setViewValue(newVal);\n            _maxsize(newVal);\n            _minsize(newVal);\n            _maxnum(newVal);\n            _minnum(newVal);\n            _accept(newVal);\n          }\n\n          function _attachHandlerForEvent(eventName, handler, fReader, file, fileObject) {\n            fReader[eventName] = function(e) {\n              handler()(e, fReader, file, rawFiles, fileObjects, fileObject);\n            };\n          }\n\n          function _readerOnLoad(fReader, file, fileObject) {\n\n            return function(e) {\n\n              var buffer = e.target.result;\n              var promise;\n\n              // do not convert the image to base64 if it exceeds the maximum\n              // size to prevent the browser from freezing\n              var exceedsMaxSize = attrs.maxsize && file.size > attrs.maxsize * 1024;\n              if (attrs.doNotParseIfOversize !== undefined && exceedsMaxSize) {\n                fileObject.base64 = null;\n              } else {\n                fileObject.base64 = $window._arrayBufferToBase64(buffer);\n              }\n\n              if (attrs.parser) {\n                promise = $q.when(scope.parser()(file, fileObject));\n              } else {\n                promise = $q.when(fileObject);\n              }\n\n              promise.then(function(fileObj) {\n                fileObjects.push(fileObj);\n                // fulfill the promise here.\n                file.deferredObj.resolve();\n              });\n\n              if (attrs.onload) {\n                if (scope.onload && typeof scope.onload() === \"function\") {\n                  scope.onload()(e, fReader, file, rawFiles, fileObjects, fileObject);\n                } else {\n                  scope.onload(e, rawFiles);\n                }\n              }\n\n            };\n\n          }\n\n          function _attachEventHandlers(fReader, file, fileObject) {\n\n            for (var i = FILE_READER_EVENTS.length - 1; i >= 0; i--) {\n              var e = FILE_READER_EVENTS[i];\n              if (attrs[e] && e !== 'onload') { // don't attach handler to onload yet\n                _attachHandlerForEvent(e, scope[e], fReader, file, fileObject);\n              }\n            }\n\n            fReader.onload = _readerOnLoad(fReader, file, fileObject);\n          }\n\n          function _readFiles() {\n            var promises = [];\n            var i;\n            for (i = rawFiles.length - 1; i >= 0; i--) {\n              // append file a new promise, that waits until resolved\n              rawFiles[i].deferredObj = $q.defer();\n              promises.push(rawFiles[i].deferredObj.promise);\n              // TODO: Make sure all promises are resolved even during file reader error, otherwise view value wont be updated\n            }\n\n            // set view value once all files are read\n            $q.all(promises).then(_setViewValue);\n\n            for (i = rawFiles.length - 1; i >= 0; i--) {\n              var reader = new $window.FileReader();\n              var file = rawFiles[i];\n              var fileObject = {};\n\n              fileObject.filetype = file.type;\n              fileObject.filename = file.name;\n              fileObject.filesize = file.size;\n\n              _attachEventHandlers(reader, file, fileObject);\n              reader.readAsArrayBuffer(file);\n            }\n          }\n\n          function _onChange(e) {\n            if (attrs.onChange) {\n              if (scope.onChange && typeof scope.onChange() === \"function\") {\n                scope.onChange()(e, rawFiles);\n              } else {\n                scope.onChange(e, rawFiles);\n              }\n            }\n          }\n\n          function _onAfterValidate(e) {\n            if (attrs.onAfterValidate) {\n              // wait for all promises, in rawFiles,\n              //   then call onAfterValidate\n              var promises = [];\n              for (var i = rawFiles.length - 1; i >= 0; i--) {\n                promises.push(rawFiles[i].deferredObj.promise);\n              }\n              $q.all(promises).then(function() {\n                if (scope.onAfterValidate && typeof scope.onAfterValidate() === \"function\") {\n                  scope.onAfterValidate()(e, fileObjects, rawFiles);\n                } else {\n                  scope.onAfterValidate(e, fileObjects, rawFiles);\n                }\n              });\n            }\n          }\n\n          ngModel.$isEmpty = function(val) {\n            return !val || (angular.isArray(val) ? val.length === 0 : !val.base64);\n          };\n\n          // http://stackoverflow.com/questions/1703228/how-can-i-clear-an-html-file-input-with-javascript\n          scope._clearInput = function() {\n            elem[0].value = '';\n          };\n\n          scope.$watch(function() {\n            return ngModel.$viewValue;\n          }, function(val) {\n            if (ngModel.$isEmpty(val) && ngModel.$dirty) {\n              scope._clearInput();\n              // Remove validation errors\n              ngModel.$setValidity('maxnum', true);\n              ngModel.$setValidity('minnum', true);\n              ngModel.$setValidity('maxsize', true);\n              ngModel.$setValidity('minsize', true);\n              ngModel.$setValidity('accept', true);\n            }\n          });\n\n          elem.on('change', function(e) {\n\n            fileObjects = [];\n            fileObjects = angular.copy(fileObjects);\n\n            if (e.target.files.length === 0) {\n              rawFiles = [];\n              _setViewValue();\n            } else {\n              rawFiles = e.target.files; // use event target so we can mock the files from test\n              _readFiles();\n              _onChange(e);\n              _onAfterValidate(e);\n            }\n\n            if (attrs.allowSameFile) {\n              scope._clearInput();\n            }\n\n          });\n\n        }\n      };\n\n    }\n  ]);\n\n})(window);\n"]}