(function() { 'use strict'; angular .module("agora.plus.formComponent") .directive("profilePicUploadComp", profilePicUploadComp) .factory("fileReader", fileReader); function profilePicUploadComp(fileReader, $q) { var NOT_AN_IMAGE = "asdasd"; var createResolvedPromise = function() { var d = $q.defer(); d.resolve(); return d.promise; }; return { restrict: "A", require: "ngModel", scope: { image: "=ngModel" }, link: linking, replace: true } linking.$inject = ["scope", "elem", "attrs", "ngModel"]; function linking(scope, elem, attrs, ngModel) { elem.bind("change",appliedFunc); /** * appliedFunc Function used onchange in input type file for the * profile picture. * Sets the model view value with the loaded file data * @param {Object} event Onchange event */ function appliedFunc(event) { var file = (event.srcElement || event.target).files[0]; ngModel.$setViewValue(file, 'change'); } /** * Array of parsers for the model. In this particular case * the loade file is parsed and its data read as data url * @param {Object} file File descriptor * @return {File|Promise} Promise or the file itself */ ngModel.$parsers.push(function(file) { if (!file) { return file; } return { fileReaderPromise: fileReader.readAsDataUrl(file, scope) }; }); /*scope.$watch('image', function(value) { if (value && typeof value === 'string') { scope.image = { src: value, isPath: true, }; } });*/ /* Model validators */ ngModel.$validators.image = function(modelValue, viewValue) { var value = modelValue || viewValue; return value !== NOT_AN_IMAGE; }; ngModel.$asyncValidators.parsing = function(modelValue, viewValue) { var value = modelValue || viewValue; if (!value || !value.fileReaderPromise) { return createResolvedPromise(); } // This should help keep the model value clean. value.fileReaderPromise.finally(function() { delete value.fileReaderPromise; }); return value.fileReaderPromise.then(function(dataUrl) { value.src = dataUrl; }, function() { return $q.reject('Failed to parse'); }); }; ngModel.$asyncValidators.dimensions = function(modelValue, viewValue) { if (!attrs.dimensions) { return createResolvedPromise(); } var value = modelValue || viewValue; if (!value || !value.fileReaderPromise) { return createResolvedPromise(); } var deferred = $q.defer(); value.fileReaderPromise.then(function(dataUrl) { // creating an image lets us find out its dimensions after it's loaded var image = document.createElement('img'); image.addEventListener('load', function() { var valid = scope.dimensionRestrictions({ width: image.width, height: image.height, }); scope.$apply(function() { if (valid) { deferred.resolve(); } else { deferred.reject('Invalid dimensions'); } }); }); image.addEventListener('error', function() { scope.$apply(function() { deferred.reject('Failed to detect dimensions. Not an image!'); }); }); image.src = dataUrl; }, function() { deferred.reject('Failed to detect dimensions'); }); return deferred.promise; }; } function profilePicUploadComp() {} } /** * Credits to K. Scott Allen - OdeToCode * https://odetocode.com/blogs/scott/archive/2013/07/03/building-a-filereader-service-for-angularjs-the-service.aspx */ function fileReader($q) { /** * onLoad Callback function used when the load file is triggered * @param {FileReader} reader FileReader instance * @param {Object} deferred Promise object * @param {Object} scope Scope object * @return {Function} On load callback */ var onLoad = function(reader, deferred, scope) { return function() { scope.$apply(function() { deferred.resolve(reader.result); }); }; }; /** * onError Callback function used when an error is triggered * @param {FileReader} reader FileReader instance * @param {Object} deferred Promise object * @param {Object} scope Scope object * @return {Function} On error callback */ var onError = function(reader, deferred, scope) { return function() { scope.$apply(function() { deferred.reject(reader.result); }); }; }; /** * onProgress On progress event for file reader * Transforms the event so that it can be used by other parties * @param {Object} reader Reader instance * @param {Object} scope Current scope * @return {Function} on progress event listener. * */ var onProgress = function(reader, scope) { return function (event) { scope.$broadcast("fileProgress", { total: event.total, loaded: event.loaded }); }; }; /** * getReader Get a reader instance * @param {Object} deferred Promise * @param {Object} scope Scope object * @return {FileReader} An instance of FileReader */ var getReader = function(deferred, scope) { var reader = new FileReader(); reader.onload = onLoad(reader, deferred, scope); reader.onerror = onError(reader, deferred, scope); // reader.onprogress = onProgress(reader, deferred, scope); return reader; }; /** * readAsDataURL reads the data file as data Url. When the ready state * becomes DONE the load ends. The return of the method readAsDataURL results is getting * the data as a URL representing the file's data as a base64 encoded string. * @param {[type]} file [description] * @param {[type]} scope [description] * @return {[type]} [description] */ var readAsDataURL = function(file, scope) { var deferred = $q.defer(), reader = getReader(deferred, scope); reader.readAsDataURL(file); return deferred.promise; }; return { readAsDataUrl: readAsDataURL }; } }());