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