e031ed0e17ecc3bda9fdcf72cac6d7ae67a85bbd
[portal.git] / ecomp-portal-FE-common / client / app / directives / image-upload / image-upload.directive.js
1 /*-
2  * ================================================================================
3  * eCOMP Portal
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ================================================================================
19  */
20
21 'use strict';
22
23 angular.module('ecompApp').directive('imageUpload', function factory($q) {
24     var imageMimeRgx = /^image\/[a-zA-Z0-9]*$/;
25
26     var URL = window.URL || window.webkitURL;
27
28     var getResizeArea = function () {
29         var resizeAreaId = 'fileupload-resize-area';
30
31         var resizeArea = document.getElementById(resizeAreaId);
32
33         if (!resizeArea) {
34             resizeArea = document.createElement('canvas');
35             resizeArea.id = resizeAreaId;
36             resizeArea.style.visibility = 'hidden';
37             document.body.appendChild(resizeArea);
38         }
39
40         return resizeArea;
41     };
42
43     var resizeImage = function (origImage, options) {
44         var maxHeight = options.resizeMaxHeight || 300;
45         var maxWidth = options.resizeMaxWidth || 250;
46         var quality = options.resizeQuality || 0.7;
47         var type = options.resizeType || 'image/jpg';
48
49         var canvas = getResizeArea();
50
51         var height = origImage.height;
52         var width = origImage.width;
53
54         //image redraw starting points
55         var x0, y0;
56
57         // calculate the width and height, constraining the proportions
58         if (width > height) {
59             if (width > maxWidth) {
60                 height = Math.round(height *= maxWidth / width);
61                 width = maxWidth;
62
63                 x0 = 0;
64                 y0 = Math.round((maxHeight - height)/2);
65             }else{
66                 maxHeight = height;
67                 maxWidth = width;
68                 x0 = 0;
69                 y0 = 0;
70             }
71         } else {
72             if (height > maxHeight) {
73                 width = Math.round(width *= maxHeight / height);
74                 height = maxHeight;
75
76                 x0 = Math.round((maxWidth - width)/2);
77                 y0 = 0;
78             }else{
79                 maxHeight = height;
80                 maxWidth = width;
81                 x0 = 0;
82                 y0 = 0;
83             }
84         }
85
86         canvas.width = maxWidth;
87         canvas.height = maxHeight;
88
89         //draw image on canvas
90         var ctx = canvas.getContext('2d');
91
92         //set background color
93         if(options.backgroundColor){
94             ctx.fillStyle = options.backgroundColor;
95             ctx.fillRect(0,0,maxWidth,maxHeight);
96         }
97
98
99         ctx.drawImage(origImage, x0, y0, width, height);
100
101         // get the data from canvas as 70% jpg (or specified type).
102         return canvas.toDataURL(type, quality);
103     };
104
105     var createImage = function(url, callback) {
106         var image = new Image();
107         image.onload = function() {
108             callback(image);
109         };
110         image.src = url;
111     };
112
113     var fileToDataURL = function (file) {
114         var deferred = $q.defer();
115         var reader = new FileReader();
116         reader.onload = function (e) {
117             deferred.resolve(e.target.result);
118         };
119         reader.readAsDataURL(file);
120         return deferred.promise;
121     };
122
123     /**
124      * Image Upload directive
125      * ************************
126      * image-upload: image object , Mandatory
127      * image-upload-resize-max-height: <Number>, Optional (default 300), resize maximum height
128      * image-upload-resize-max-width: <Number>, Optional (default 270), resize maximum width
129      * image-upload-resize-quality: <Number>, Optional, value can be 0.0-1.0 (default 0.7), resize compression quality
130      * image-upload-resize-type: <String>, Optional, (default 'image/jpg'), image mime type
131      * image-upload-api: <Object>, Optional, pass an api  reference object and get api.clearFile() function - clear input field and reset form validation
132      * image-upload-background-color: <String> color name, Optional, background color fill if image doesn't fit the whole desired resize area.
133      *
134      * in addition, if <input> element is part of <form>, in order to get form validation please  set its 'name' attribute and 'ng-model'  to get the following field validation errors:
135      * - 'mimeType' : in case the uploaded image is not an image
136      * - 'imageSize' : in case the image size (in bytes) is too large
137      */
138
139     return {
140         restrict: 'A',
141         require: '^form',
142         scope: {
143             image: '=imageUpload',
144             resizeMaxHeight: '@?imageUploadResizeMaxHeight',
145             resizeMaxWidth: '@?imageUploadResizeMaxWidth',
146             resizeQuality: '@?imageUploadResizeQuality',
147             resizeType: '@?imageUploadResizeType',
148             imageApi: '=?imageUploadApi',
149             backgroundColor: '@?imageUploadBackgroundColor'
150         },
151         compile: function compile(tElement, tAttrs, transclude) {
152             return function postLink(scope, iElement, iAttrs, formCtrl) {
153                 var doResizing = function(imageResult, callback) {
154                     createImage(imageResult.url, function(image) {
155                         var dataURL = resizeImage(image, scope);
156                         imageResult.resized = {
157                             dataURL: dataURL,
158                             type: dataURL.match(/:(.+\/.+);/)[1]
159                         };
160                         callback(imageResult);
161                     });
162                 };
163
164                 var applyScope = function(imageResult) {
165                     scope.$apply(function() {
166                         //console.log(imageResult);
167                         if(iAttrs.multiple)
168                             scope.image.push(imageResult);
169                         else
170                             scope.image = imageResult;
171                     });
172                 };
173
174                 iElement.bind('change', function (evt) {
175                     //when multiple always return an array of images
176                     if(iAttrs.multiple)
177                         scope.image = [];
178
179                     var files = evt.target.files;
180                     for(var i = 0; i < files.length; i++) {
181                         setInputValidity(files[i]);
182
183                         //create a result object for each file in files
184                         var imageResult = {
185                             file: files[i],
186                             url: URL.createObjectURL(files[i])
187                         };
188
189                         fileToDataURL(files[i]).then(function (dataURL) {
190                             imageResult.dataURL = dataURL;
191                         });
192
193                         if(scope.resizeMaxHeight || scope.resizeMaxWidth) { //resize image
194                             doResizing(imageResult, function(imageResult) {
195                                 applyScope(imageResult);
196                             });
197                         }
198                         else { //no resizing
199                             applyScope(imageResult);
200                         }
201                     }
202                 });
203
204                 //API for otter actions
205                 scope.imageApi = scope.imageApi || {};
206                 scope.imageApi.clearFile = () => {
207                     iElement[0].value = '';
208                     setInputValidity();
209                 };
210
211
212                 let setInputValidity = file => {
213                     //if form validation supported
214
215                     if(formCtrl && iAttrs.name && formCtrl[iAttrs.name]){
216                         formCtrl[iAttrs.name].$setDirty();
217                         if(file && file.type && !imageMimeRgx.test(file.type)){
218                             //set form invalid
219                             formCtrl[iAttrs.name].$setValidity('mimeType', false);
220                             applyScope();
221                             return;
222                         }
223                         if(file && file.size && file.size > 1000000){
224                             //set form invalid
225                             formCtrl[iAttrs.name].$setValidity('imageSize', false);
226                             applyScope();
227                             return;
228                         }
229                         //set valid
230                         formCtrl[iAttrs.name].$setValidity('mimeType', true);
231                         formCtrl[iAttrs.name].$setValidity('imageSize', true);
232                     }
233
234                 }
235             }
236         }
237     }
238 });