GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

third-party/angularjs-modules-plugins/ng-file-upload-12.2.12/demo/src/main/webapp/js/ng-file-upload.js   F
last analyzed

Complexity

Total Complexity 660
Complexity/F 2.19

Size

Lines of Code 2474
Function Count 302

Duplication

Duplicated Lines 2443
Ratio 98.75 %

Importance

Changes 0
Metric Value
eloc 1659
dl 2443
loc 2474
rs 0.8
c 0
b 0
f 0
wmc 660
mnd 358
bc 358
fnc 302
bpm 1.1854
cpm 2.1854
noi 19

28 Functions

Rating   Name   Duplication   Size   Complexity  
A ng-file-upload.js ➔ checkLoadError 13 13 5
C ng-file-upload.js ➔ update 33 33 11
A ng-file-upload.js ➔ applyTransform 18 18 4
F ng-file-upload.js ➔ globStringToRegex 32 32 27
F ng-file-upload.js ➔ addFieldToFormData 40 40 14
C ng-file-upload.js ➔ resizeWithParams 29 29 10
C ng-file-upload.js ➔ digestConfig 29 29 9
F ng-file-upload.js ➔ markModelAsDirty 9 9 15
B ng-file-upload.js ➔ toResumeFile 21 21 7
F ng-file-upload.js ➔ arrayBufferToBase64 9 9 14
F ng-file-upload.js ➔ getTagType 6 6 37
C ng-file-upload.js ➔ success 8 8 11
F ng-file-upload.js ➔ applyExifRotations 11 11 44
A ng-file-upload.js ➔ toArray 3 3 2
D ng-file-upload.js ➔ isDelayedClickSupported 11 11 13
A ng-file-upload.js ➔ error 4 4 1
F ng-file-upload.js ➔ sendHttp 162 162 56
B ng-file-upload.js ➔ copy 9 9 8
F ng-file-upload.js ➔ validateAsync 85 85 33
F ng-file-upload.js ➔ linkDrop 306 306 139
B ng-file-upload.js ➔ resizeFile 18 18 7
A ng-file-upload.js ➔ checkLoadErrorInCaseOfNoCallback 13 13 5
F ng-file-upload.js ➔ resizeAndUpdate 35 35 15
C ng-file-upload.js ➔ removeDuplicates 33 33 10
A ng-file-upload.js ➔ dropAvailable 4 4 1
F ng-file-upload.js ➔ linkFileSelect 230 230 56
F ng-file-upload.js ➔ validateSync 32 32 21
F ng-file-upload.js ➔ linkFileDirective 72 72 20

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like third-party/angularjs-modules-plugins/ng-file-upload-12.2.12/demo/src/main/webapp/js/ng-file-upload.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**!
2
 * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort,
3
 * progress, resize, thumbnail, preview, validation and CORS
4
 * @author  Danial  <[email protected]>
5
 * @version 12.2.12
6
 */
7
8
if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) {
9
  window.XMLHttpRequest.prototype.setRequestHeader = (function (orig) {
10
    return function (header, value) {
11
      if (header === '__setXHR_') {
12
        var val = value(this);
13
        // fix for angular < 1.2.0
14
        if (val instanceof Function) {
15
          val(this);
16
        }
17
      } else {
18
        orig.apply(this, arguments);
19
      }
20
    };
21
  })(window.XMLHttpRequest.prototype.setRequestHeader);
22
}
23
24
var ngFileUpload = angular.module('ngFileUpload', []);
25
26
ngFileUpload.version = '12.2.12';
27
28 View Code Duplication
ngFileUpload.service('UploadBase', ['$http', '$q', '$timeout', function ($http, $q, $timeout) {
29
  var upload = this;
30
  upload.promisesCount = 0;
31
32
  this.isResumeSupported = function () {
33
    return window.Blob && window.Blob.prototype.slice;
34
  };
35
36
  var resumeSupported = this.isResumeSupported();
37
38
  function sendHttp(config) {
39
    config.method = config.method || 'POST';
40
    config.headers = config.headers || {};
41
42
    var deferred = config._deferred = config._deferred || $q.defer();
43
    var promise = deferred.promise;
44
45
    function notifyProgress(e) {
46
      if (deferred.notify) {
47
        deferred.notify(e);
48
      }
49
      if (promise.progressFunc) {
50
        $timeout(function () {
51
          promise.progressFunc(e);
52
        });
53
      }
54
    }
55
56
    function getNotifyEvent(n) {
57
      if (config._start != null && resumeSupported) {
58
        return {
59
          loaded: n.loaded + config._start,
60
          total: (config._file && config._file.size) || n.total,
61
          type: n.type, config: config,
62
          lengthComputable: true, target: n.target
63
        };
64
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
65
        return n;
66
      }
67
    }
68
69
    if (!config.disableProgress) {
70
      config.headers.__setXHR_ = function () {
71
        return function (xhr) {
72
          if (!xhr || !xhr.upload || !xhr.upload.addEventListener) return;
73
          config.__XHR = xhr;
74
          if (config.xhrFn) config.xhrFn(xhr);
75
          xhr.upload.addEventListener('progress', function (e) {
76
            e.config = config;
77
            notifyProgress(getNotifyEvent(e));
78
          }, false);
79
          //fix for firefox not firing upload progress end, also IE8-9
80
          xhr.upload.addEventListener('load', function (e) {
81
            if (e.lengthComputable) {
82
              e.config = config;
83
              notifyProgress(getNotifyEvent(e));
84
            }
85
          }, false);
86
        };
87
      };
88
    }
89
90
    function uploadWithAngular() {
91
      $http(config).then(function (r) {
92
          if (resumeSupported && config._chunkSize && !config._finished && config._file) {
93
            var fileSize = config._file && config._file.size || 0;
94
            notifyProgress({
95
                loaded: Math.min(config._end, fileSize),
96
                total: fileSize,
97
                config: config,
98
                type: 'progress'
99
              }
100
            );
101
            upload.upload(config, true);
102
          } else {
103
            if (config._finished) delete config._finished;
104
            deferred.resolve(r);
105
          }
106
        }, function (e) {
107
          deferred.reject(e);
108
        }, function (n) {
109
          deferred.notify(n);
110
        }
111
      );
112
    }
113
114
    if (!resumeSupported) {
115
      uploadWithAngular();
116
    } else if (config._chunkSize && config._end && !config._finished) {
117
      config._start = config._end;
118
      config._end += config._chunkSize;
119
      uploadWithAngular();
120
    } else if (config.resumeSizeUrl) {
121
      $http.get(config.resumeSizeUrl).then(function (resp) {
122
        if (config.resumeSizeResponseReader) {
123
          config._start = config.resumeSizeResponseReader(resp.data);
124
        } else {
125
          config._start = parseInt((resp.data.size == null ? resp.data : resp.data.size).toString());
126
        }
127
        if (config._chunkSize) {
128
          config._end = config._start + config._chunkSize;
129
        }
130
        uploadWithAngular();
131
      }, function (e) {
132
        throw e;
133
      });
134
    } else if (config.resumeSize) {
135
      config.resumeSize().then(function (size) {
136
        config._start = size;
137
        if (config._chunkSize) {
138
          config._end = config._start + config._chunkSize;
139
        }
140
        uploadWithAngular();
141
      }, function (e) {
142
        throw e;
143
      });
144
    } else {
145
      if (config._chunkSize) {
146
        config._start = 0;
147
        config._end = config._start + config._chunkSize;
148
      }
149
      uploadWithAngular();
150
    }
151
152
153
    promise.success = function (fn) {
154
      promise.then(function (response) {
155
        fn(response.data, response.status, response.headers, config);
156
      });
157
      return promise;
158
    };
159
160
    promise.error = function (fn) {
161
      promise.then(null, function (response) {
162
        fn(response.data, response.status, response.headers, config);
163
      });
164
      return promise;
165
    };
166
167
    promise.progress = function (fn) {
168
      promise.progressFunc = fn;
169
      promise.then(null, null, function (n) {
170
        fn(n);
171
      });
172
      return promise;
173
    };
174
    promise.abort = promise.pause = function () {
175
      if (config.__XHR) {
176
        $timeout(function () {
177
          config.__XHR.abort();
178
        });
179
      }
180
      return promise;
181
    };
182
    promise.xhr = function (fn) {
183
      config.xhrFn = (function (origXhrFn) {
184
        return function () {
185
          if (origXhrFn) origXhrFn.apply(promise, arguments);
186
          fn.apply(promise, arguments);
187
        };
188
      })(config.xhrFn);
189
      return promise;
190
    };
191
192
    upload.promisesCount++;
193
    if (promise['finally'] && promise['finally'] instanceof Function) {
194
      promise['finally'](function () {
195
        upload.promisesCount--;
196
      });
197
    }
198
    return promise;
199
  }
200
201
  this.isUploadInProgress = function () {
202
    return upload.promisesCount > 0;
203
  };
204
205
  this.rename = function (file, name) {
206
    file.ngfName = name;
207
    return file;
208
  };
209
210
  this.jsonBlob = function (val) {
211
    if (val != null && !angular.isString(val)) {
212
      val = JSON.stringify(val);
213
    }
214
    var blob = new window.Blob([val], {type: 'application/json'});
215
    blob._ngfBlob = true;
216
    return blob;
217
  };
218
219
  this.json = function (val) {
220
    return angular.toJson(val);
221
  };
222
223
  function copy(obj) {
224
    var clone = {};
225
    for (var key in obj) {
226
      if (obj.hasOwnProperty(key)) {
227
        clone[key] = obj[key];
228
      }
229
    }
230
    return clone;
231
  }
232
233
  this.isFile = function (file) {
234
    return file != null && (file instanceof window.Blob || (file.flashId && file.name && file.size));
235
  };
236
237
  this.upload = function (config, internal) {
238
    function toResumeFile(file, formData) {
239
      if (file._ngfBlob) return file;
240
      config._file = config._file || file;
241
      if (config._start != null && resumeSupported) {
242
        if (config._end && config._end >= file.size) {
243
          config._finished = true;
244
          config._end = file.size;
245
        }
246
        var slice = file.slice(config._start, config._end || file.size);
247
        slice.name = file.name;
248
        slice.ngfName = file.ngfName;
249
        if (config._chunkSize) {
250
          formData.append('_chunkSize', config._chunkSize);
251
          formData.append('_currentChunkSize', config._end - config._start);
252
          formData.append('_chunkNumber', Math.floor(config._start / config._chunkSize));
253
          formData.append('_totalSize', config._file.size);
254
        }
255
        return slice;
256
      }
257
      return file;
258
    }
259
260
    function addFieldToFormData(formData, val, key) {
261
      if (val !== undefined) {
262
        if (angular.isDate(val)) {
263
          val = val.toISOString();
264
        }
265
        if (angular.isString(val)) {
266
          formData.append(key, val);
267
        } else if (upload.isFile(val)) {
268
          var file = toResumeFile(val, formData);
269
          var split = key.split(',');
270
          if (split[1]) {
271
            file.ngfName = split[1].replace(/^\s+|\s+$/g, '');
272
            key = split[0];
273
          }
274
          config._fileKey = config._fileKey || key;
275
          formData.append(key, file, file.ngfName || file.name);
276
        } else {
277
          if (angular.isObject(val)) {
278
            if (val.$$ngfCircularDetection) throw 'ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: ' + key;
279
280
            val.$$ngfCircularDetection = true;
281
            try {
282
              for (var k in val) {
283
                if (val.hasOwnProperty(k) && k !== '$$ngfCircularDetection') {
284
                  var objectKey = config.objectKey == null ? '[i]' : config.objectKey;
285
                  if (val.length && parseInt(k) > -1) {
286
                    objectKey = config.arrayKey == null ? objectKey : config.arrayKey;
287
                  }
288
                  addFieldToFormData(formData, val[k], key + objectKey.replace(/[ik]/g, k));
289
                }
290
              }
291
            } finally {
292
              delete val.$$ngfCircularDetection;
293
            }
294
          } else {
295
            formData.append(key, val);
296
          }
297
        }
298
      }
299
    }
300
301
    function digestConfig() {
302
      config._chunkSize = upload.translateScalars(config.resumeChunkSize);
303
      config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null;
304
305
      config.headers = config.headers || {};
306
      config.headers['Content-Type'] = undefined;
307
      config.transformRequest = config.transformRequest ?
308
        (angular.isArray(config.transformRequest) ?
309
          config.transformRequest : [config.transformRequest]) : [];
310
      config.transformRequest.push(function (data) {
311
        var formData = new window.FormData(), key;
312
        data = data || config.fields || {};
313
        if (config.file) {
314
          data.file = config.file;
315
        }
316
        for (key in data) {
317
          if (data.hasOwnProperty(key)) {
318
            var val = data[key];
319
            if (config.formDataAppender) {
320
              config.formDataAppender(formData, key, val);
321
            } else {
322
              addFieldToFormData(formData, val, key);
323
            }
324
          }
325
        }
326
327
        return formData;
328
      });
329
    }
330
331
    if (!internal) config = copy(config);
332
    if (!config._isDigested) {
333
      config._isDigested = true;
334
      digestConfig();
335
    }
336
337
    return sendHttp(config);
338
  };
339
340
  this.http = function (config) {
341
    config = copy(config);
342
    config.transformRequest = config.transformRequest || function (data) {
343
        if ((window.ArrayBuffer && data instanceof window.ArrayBuffer) || data instanceof window.Blob) {
344
          return data;
345
        }
346
        return $http.defaults.transformRequest[0].apply(this, arguments);
347
      };
348
    config._chunkSize = upload.translateScalars(config.resumeChunkSize);
349
    config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null;
350
351
    return sendHttp(config);
352
  };
353
354
  this.translateScalars = function (str) {
355
    if (angular.isString(str)) {
356
      if (str.search(/kb/i) === str.length - 2) {
357
        return parseFloat(str.substring(0, str.length - 2) * 1024);
358
      } else if (str.search(/mb/i) === str.length - 2) {
359
        return parseFloat(str.substring(0, str.length - 2) * 1048576);
360
      } else if (str.search(/gb/i) === str.length - 2) {
361
        return parseFloat(str.substring(0, str.length - 2) * 1073741824);
362
      } else if (str.search(/b/i) === str.length - 1) {
363
        return parseFloat(str.substring(0, str.length - 1));
364
      } else if (str.search(/s/i) === str.length - 1) {
365
        return parseFloat(str.substring(0, str.length - 1));
366
      } else if (str.search(/m/i) === str.length - 1) {
367
        return parseFloat(str.substring(0, str.length - 1) * 60);
368
      } else if (str.search(/h/i) === str.length - 1) {
369
        return parseFloat(str.substring(0, str.length - 1) * 3600);
370
      }
371
    }
372
    return str;
373
  };
374
375
  this.urlToBlob = function(url) {
376
    var defer = $q.defer();
377
    $http({url: url, method: 'get', responseType: 'arraybuffer'}).then(function (resp) {
378
      var arrayBufferView = new Uint8Array(resp.data);
379
      var type = resp.headers('content-type') || 'image/WebP';
380
      var blob = new window.Blob([arrayBufferView], {type: type});
381
      var matches = url.match(/.*\/(.+?)(\?.*)?$/);
382
      if (matches.length > 1) {
383
        blob.name = matches[1];
384
      }
385
      defer.resolve(blob);
386
    }, function (e) {
387
      defer.reject(e);
388
    });
389
    return defer.promise;
390
  };
391
392
  this.setDefaults = function (defaults) {
393
    this.defaults = defaults || {};
394
  };
395
396
  this.defaults = {};
397
  this.version = ngFileUpload.version;
398
}
399
400
]);
401
402 View Code Duplication
ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', '$q', 'UploadExif', function ($parse, $timeout, $compile, $q, UploadExif) {
403
  var upload = UploadExif;
404
  upload.getAttrWithDefaults = function (attr, name) {
405
    if (attr[name] != null) return attr[name];
406
    var def = upload.defaults[name];
407
    return (def == null ? def : (angular.isString(def) ? def : JSON.stringify(def)));
408
  };
409
410
  upload.attrGetter = function (name, attr, scope, params) {
411
    var attrVal = this.getAttrWithDefaults(attr, name);
412
    if (scope) {
413
      try {
414
        if (params) {
415
          return $parse(attrVal)(scope, params);
416
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
417
          return $parse(attrVal)(scope);
418
        }
419
      } catch (e) {
420
        // hangle string value without single qoute
421
        if (name.search(/min|max|pattern/i)) {
422
          return attrVal;
423
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
424
          throw e;
425
        }
426
      }
427
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
428
      return attrVal;
429
    }
430
  };
431
432
  upload.shouldUpdateOn = function (type, attr, scope) {
433
    var modelOptions = upload.attrGetter('ngfModelOptions', attr, scope);
434
    if (modelOptions && modelOptions.updateOn) {
435
      return modelOptions.updateOn.split(' ').indexOf(type) > -1;
436
    }
437
    return true;
438
  };
439
440
  upload.emptyPromise = function () {
441
    var d = $q.defer();
442
    var args = arguments;
443
    $timeout(function () {
444
      d.resolve.apply(d, args);
445
    });
446
    return d.promise;
447
  };
448
449
  upload.rejectPromise = function () {
450
    var d = $q.defer();
451
    var args = arguments;
452
    $timeout(function () {
453
      d.reject.apply(d, args);
454
    });
455
    return d.promise;
456
  };
457
458
  upload.happyPromise = function (promise, data) {
459
    var d = $q.defer();
460
    promise.then(function (result) {
461
      d.resolve(result);
462
    }, function (error) {
463
      $timeout(function () {
464
        throw error;
465
      });
466
      d.resolve(data);
467
    });
468
    return d.promise;
469
  };
470
471
  function applyExifRotations(files, attr, scope) {
472
    var promises = [upload.emptyPromise()];
473
    angular.forEach(files, function (f, i) {
474
      if (f.type.indexOf('image/jpeg') === 0 && upload.attrGetter('ngfFixOrientation', attr, scope, {$file: f})) {
475
        promises.push(upload.happyPromise(upload.applyExifRotation(f), f).then(function (fixedFile) {
476
          files.splice(i, 1, fixedFile);
477
        }));
478
      }
479
    });
480
    return $q.all(promises);
481
  }
482
483
  function resizeFile(files, attr, scope, ngModel) {
484
    var resizeVal = upload.attrGetter('ngfResize', attr, scope);
485
    if (!resizeVal || !upload.isResizeSupported() || !files.length) return upload.emptyPromise();
486
    if (resizeVal instanceof Function) {
487
      var defer = $q.defer();
488
      return resizeVal(files).then(function (p) {
489
        resizeWithParams(p, files, attr, scope, ngModel).then(function (r) {
490
          defer.resolve(r);
491
        }, function (e) {
492
          defer.reject(e);
493
        });
494
      }, function (e) {
495
        defer.reject(e);
496
      });
497
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
498
      return resizeWithParams(resizeVal, files, attr, scope, ngModel);
499
    }
500
  }
501
502
  function resizeWithParams(params, files, attr, scope, ngModel) {
503
    var promises = [upload.emptyPromise()];
504
505
    function handleFile(f, i) {
506
      if (f.type.indexOf('image') === 0) {
507
        if (params.pattern && !upload.validatePattern(f, params.pattern)) return;
508
        params.resizeIf = function (width, height) {
509
          return upload.attrGetter('ngfResizeIf', attr, scope,
510
            {$width: width, $height: height, $file: f});
511
        };
512
        var promise = upload.resize(f, params);
513
        promises.push(promise);
514
        promise.then(function (resizedFile) {
515
          files.splice(i, 1, resizedFile);
516
        }, function (e) {
517
          f.$error = 'resize';
518
          (f.$errorMessages = (f.$errorMessages || {})).resize = true;
519
          f.$errorParam = (e ? (e.message ? e.message : e) + ': ' : '') + (f && f.name);
520
          ngModel.$ngfValidations.push({name: 'resize', valid: false});
521
          upload.applyModelValidation(ngModel, files);
522
        });
523
      }
524
    }
525
526
    for (var i = 0; i < files.length; i++) {
527
      handleFile(files[i], i);
528
    }
529
    return $q.all(promises);
530
  }
531
532
  upload.updateModel = function (ngModel, attr, scope, fileChange, files, evt, noDelay) {
533
    function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
534
      attr.$$ngfPrevValidFiles = files;
535
      attr.$$ngfPrevInvalidFiles = invalidFiles;
536
      var file = files && files.length ? files[0] : null;
537
      var invalidFile = invalidFiles && invalidFiles.length ? invalidFiles[0] : null;
538
539
      if (ngModel) {
540
        upload.applyModelValidation(ngModel, files);
541
        ngModel.$setViewValue(isSingleModel ? file : files);
542
      }
543
544
      if (fileChange) {
545
        $parse(fileChange)(scope, {
546
          $files: files,
547
          $file: file,
548
          $newFiles: newFiles,
549
          $duplicateFiles: dupFiles,
550
          $invalidFiles: invalidFiles,
551
          $invalidFile: invalidFile,
552
          $event: evt
553
        });
554
      }
555
556
      var invalidModel = upload.attrGetter('ngfModelInvalid', attr);
557
      if (invalidModel) {
558
        $timeout(function () {
559
          $parse(invalidModel).assign(scope, isSingleModel ? invalidFile : invalidFiles);
560
        });
561
      }
562
      $timeout(function () {
563
        // scope apply changes
564
      });
565
    }
566
567
    var allNewFiles, dupFiles = [], prevValidFiles, prevInvalidFiles,
568
      invalids = [], valids = [];
569
570
    function removeDuplicates() {
571
      function equals(f1, f2) {
572
        return f1.name === f2.name && (f1.$ngfOrigSize || f1.size) === (f2.$ngfOrigSize || f2.size) &&
573
          f1.type === f2.type;
574
      }
575
576
      function isInPrevFiles(f) {
577
        var j;
578
        for (j = 0; j < prevValidFiles.length; j++) {
579
          if (equals(f, prevValidFiles[j])) {
580
            return true;
581
          }
582
        }
583
        for (j = 0; j < prevInvalidFiles.length; j++) {
584
          if (equals(f, prevInvalidFiles[j])) {
585
            return true;
586
          }
587
        }
588
        return false;
589
      }
590
591
      if (files) {
592
        allNewFiles = [];
593
        dupFiles = [];
594
        for (var i = 0; i < files.length; i++) {
595
          if (isInPrevFiles(files[i])) {
596
            dupFiles.push(files[i]);
597
          } else {
598
            allNewFiles.push(files[i]);
599
          }
600
        }
601
      }
602
    }
603
604
    function toArray(v) {
605
      return angular.isArray(v) ? v : [v];
606
    }
607
608
    function resizeAndUpdate() {
609
      function updateModel() {
610
        $timeout(function () {
611
          update(keep ? prevValidFiles.concat(valids) : valids,
612
            keep ? prevInvalidFiles.concat(invalids) : invalids,
613
            files, dupFiles, isSingleModel);
614
        }, options && options.debounce ? options.debounce.change || options.debounce : 0);
615
      }
616
617
      var resizingFiles = validateAfterResize ? allNewFiles : valids;
618
      resizeFile(resizingFiles, attr, scope, ngModel).then(function () {
619
        if (validateAfterResize) {
620
          upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope)
621
            .then(function (validationResult) {
622
              valids = validationResult.validsFiles;
623
              invalids = validationResult.invalidsFiles;
624
              updateModel();
625
            });
626
        } else {
627
          updateModel();
628
        }
629
      }, function () {
630
        for (var i = 0; i < resizingFiles.length; i++) {
631
          var f = resizingFiles[i];
632
          if (f.$error === 'resize') {
633
            var index = valids.indexOf(f);
634
            if (index > -1) {
635
              valids.splice(index, 1);
636
              invalids.push(f);
637
            }
638
            updateModel();
639
          }
640
        }
641
      });
642
    }
643
644
    prevValidFiles = attr.$$ngfPrevValidFiles || [];
645
    prevInvalidFiles = attr.$$ngfPrevInvalidFiles || [];
646
    if (ngModel && ngModel.$modelValue) {
647
      prevValidFiles = toArray(ngModel.$modelValue);
648
    }
649
650
    var keep = upload.attrGetter('ngfKeep', attr, scope);
651
    allNewFiles = (files || []).slice(0);
652
    if (keep === 'distinct' || upload.attrGetter('ngfKeepDistinct', attr, scope) === true) {
653
      removeDuplicates(attr, scope);
0 ignored issues
show
Bug introduced by
The call to removeDuplicates seems to have too many arguments starting with attr.
Loading history...
654
    }
655
656
    var isSingleModel = !keep && !upload.attrGetter('ngfMultiple', attr, scope) && !upload.attrGetter('multiple', attr);
657
658
    if (keep && !allNewFiles.length) return;
659
660
    upload.attrGetter('ngfBeforeModelChange', attr, scope, {
661
      $files: files,
662
      $file: files && files.length ? files[0] : null,
663
      $newFiles: allNewFiles,
664
      $duplicateFiles: dupFiles,
665
      $event: evt
666
    });
667
668
    var validateAfterResize = upload.attrGetter('ngfValidateAfterResize', attr, scope);
669
670
    var options = upload.attrGetter('ngfModelOptions', attr, scope);
671
    upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope)
672
      .then(function (validationResult) {
673
      if (noDelay) {
674
        update(allNewFiles, [], files, dupFiles, isSingleModel);
675
      } else {
676
        if ((!options || !options.allowInvalid) && !validateAfterResize) {
677
          valids = validationResult.validFiles;
678
          invalids = validationResult.invalidFiles;
679
        } else {
680
          valids = allNewFiles;
681
        }
682
        if (upload.attrGetter('ngfFixOrientation', attr, scope) && upload.isExifSupported()) {
683
          applyExifRotations(valids, attr, scope).then(function () {
684
            resizeAndUpdate();
685
          });
686
        } else {
687
          resizeAndUpdate();
688
        }
689
      }
690
    });
691
  };
692
693
  return upload;
694
}]);
695
696 View Code Duplication
ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', function ($parse, $timeout, $compile, Upload) {
697
  var generatedElems = [];
698
699
  function isDelayedClickSupported(ua) {
700
    // fix for android native browser < 4.4 and safari windows
701
    var m = ua.match(/Android[^\d]*(\d+)\.(\d+)/);
702
    if (m && m.length > 2) {
703
      var v = Upload.defaults.androidFixMinorVersion || 4;
704
      return parseInt(m[1]) < 4 || (parseInt(m[1]) === v && parseInt(m[2]) < v);
705
    }
706
707
    // safari on windows
708
    return ua.indexOf('Chrome') === -1 && /.*Windows.*Safari.*/.test(ua);
709
  }
710
711
  function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, upload) {
712
    /** @namespace attr.ngfSelect */
713
    /** @namespace attr.ngfChange */
714
    /** @namespace attr.ngModel */
715
    /** @namespace attr.ngfModelOptions */
716
    /** @namespace attr.ngfMultiple */
717
    /** @namespace attr.ngfCapture */
718
    /** @namespace attr.ngfValidate */
719
    /** @namespace attr.ngfKeep */
720
    var attrGetter = function (name, scope) {
721
      return upload.attrGetter(name, attr, scope);
722
    };
723
724
    function isInputTypeFile() {
725
      return elem[0].tagName.toLowerCase() === 'input' && attr.type && attr.type.toLowerCase() === 'file';
726
    }
727
728
    function fileChangeAttr() {
729
      return attrGetter('ngfChange') || attrGetter('ngfSelect');
730
    }
731
732
    function changeFn(evt) {
733
      if (upload.shouldUpdateOn('change', attr, scope)) {
734
        var fileList = evt.__files_ || (evt.target && evt.target.files), files = [];
735
        /* Handle duplicate call in  IE11 */
736
        if (!fileList) return;
737
        for (var i = 0; i < fileList.length; i++) {
738
          files.push(fileList[i]);
739
        }
740
        upload.updateModel(ngModel, attr, scope, fileChangeAttr(),
741
          files.length ? files : null, evt);
742
      }
743
    }
744
745
    upload.registerModelChangeValidator(ngModel, attr, scope);
746
747
    var unwatches = [];
748
    if (attrGetter('ngfMultiple')) {
749
      unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () {
750
        fileElem.attr('multiple', attrGetter('ngfMultiple', scope));
751
      }));
752
    }
753
    if (attrGetter('ngfCapture')) {
754
      unwatches.push(scope.$watch(attrGetter('ngfCapture'), function () {
755
        fileElem.attr('capture', attrGetter('ngfCapture', scope));
756
      }));
757
    }
758
    if (attrGetter('ngfAccept')) {
759
      unwatches.push(scope.$watch(attrGetter('ngfAccept'), function () {
760
        fileElem.attr('accept', attrGetter('ngfAccept', scope));
761
      }));
762
    }
763
    unwatches.push(attr.$observe('accept', function () {
764
      fileElem.attr('accept', attrGetter('accept'));
765
    }));
766
    function bindAttrToFileInput(fileElem, label) {
767
      function updateId(val) {
768
        fileElem.attr('id', 'ngf-' + val);
769
        label.attr('id', 'ngf-label-' + val);
770
      }
771
772
      for (var i = 0; i < elem[0].attributes.length; i++) {
773
        var attribute = elem[0].attributes[i];
774
        if (attribute.name !== 'type' && attribute.name !== 'class' && attribute.name !== 'style') {
775
          if (attribute.name === 'id') {
776
            updateId(attribute.value);
777
            unwatches.push(attr.$observe('id', updateId));
778
          } else {
779
            fileElem.attr(attribute.name, (!attribute.value && (attribute.name === 'required' ||
780
            attribute.name === 'multiple')) ? attribute.name : attribute.value);
781
          }
782
        }
783
      }
784
    }
785
786
    function createFileInput() {
787
      if (isInputTypeFile()) {
788
        return elem;
789
      }
790
791
      var fileElem = angular.element('<input type="file">');
792
793
      var label = angular.element('<label>upload</label>');
794
      label.css('visibility', 'hidden').css('position', 'absolute').css('overflow', 'hidden')
795
        .css('width', '0px').css('height', '0px').css('border', 'none')
796
        .css('margin', '0px').css('padding', '0px').attr('tabindex', '-1');
797
      bindAttrToFileInput(fileElem, label);
798
799
      generatedElems.push({el: elem, ref: label});
800
801
      document.body.appendChild(label.append(fileElem)[0]);
802
803
      return fileElem;
804
    }
805
806
    function clickHandler(evt) {
807
      if (elem.attr('disabled')) return false;
808
      if (attrGetter('ngfSelectDisabled', scope)) return;
809
810
      var r = detectSwipe(evt);
811
      // prevent the click if it is a swipe
812
      if (r != null) return r;
813
814
      resetModel(evt);
815
816
      // fix for md when the element is removed from the DOM and added back #460
817
      try {
818
        if (!isInputTypeFile() && !document.body.contains(fileElem[0])) {
819
          generatedElems.push({el: elem, ref: fileElem.parent()});
820
          document.body.appendChild(fileElem.parent()[0]);
821
          fileElem.bind('change', changeFn);
822
        }
823
      } catch (e) {/*ignore*/
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
824
      }
825
826
      if (isDelayedClickSupported(navigator.userAgent)) {
827
        setTimeout(function () {
828
          fileElem[0].click();
829
        }, 0);
830
      } else {
831
        fileElem[0].click();
832
      }
833
834
      return false;
835
    }
836
837
838
    var initialTouchStartY = 0;
839
    var initialTouchStartX = 0;
840
841
    function detectSwipe(evt) {
842
      var touches = evt.changedTouches || (evt.originalEvent && evt.originalEvent.changedTouches);
843
      if (touches) {
844
        if (evt.type === 'touchstart') {
845
          initialTouchStartX = touches[0].clientX;
846
          initialTouchStartY = touches[0].clientY;
847
          return true; // don't block event default
848
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
849
          // prevent scroll from triggering event
850
          if (evt.type === 'touchend') {
851
            var currentX = touches[0].clientX;
852
            var currentY = touches[0].clientY;
853
            if ((Math.abs(currentX - initialTouchStartX) > 20) ||
854
              (Math.abs(currentY - initialTouchStartY) > 20)) {
855
              evt.stopPropagation();
856
              evt.preventDefault();
857
              return false;
858
            }
859
          }
860
          return true;
861
        }
862
      }
863
    }
864
865
    var fileElem = elem;
866
867
    function resetModel(evt) {
868
      if (upload.shouldUpdateOn('click', attr, scope) && fileElem.val()) {
869
        fileElem.val(null);
870
        upload.updateModel(ngModel, attr, scope, fileChangeAttr(), null, evt, true);
871
      }
872
    }
873
874
    if (!isInputTypeFile()) {
875
      fileElem = createFileInput();
876
    }
877
    fileElem.bind('change', changeFn);
878
879
    if (!isInputTypeFile()) {
880
      elem.bind('click touchstart touchend', clickHandler);
881
    } else {
882
      elem.bind('click', resetModel);
883
    }
884
885
    function ie10SameFileSelectFix(evt) {
886
      if (fileElem && !fileElem.attr('__ngf_ie10_Fix_')) {
887
        if (!fileElem[0].parentNode) {
888
          fileElem = null;
889
          return;
890
        }
891
        evt.preventDefault();
892
        evt.stopPropagation();
893
        fileElem.unbind('click');
894
        var clone = fileElem.clone();
895
        fileElem.replaceWith(clone);
896
        fileElem = clone;
897
        fileElem.attr('__ngf_ie10_Fix_', 'true');
898
        fileElem.bind('change', changeFn);
899
        fileElem.bind('click', ie10SameFileSelectFix);
900
        fileElem[0].click();
901
        return false;
902
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
903
        fileElem.removeAttr('__ngf_ie10_Fix_');
904
      }
905
    }
906
907
    if (navigator.appVersion.indexOf('MSIE 10') !== -1) {
908
      fileElem.bind('click', ie10SameFileSelectFix);
909
    }
910
911
    if (ngModel) ngModel.$formatters.push(function (val) {
912
      if (val == null || val.length === 0) {
913
        if (fileElem.val()) {
914
          fileElem.val(null);
915
        }
916
      }
917
      return val;
918
    });
919
920
    scope.$on('$destroy', function () {
921
      if (!isInputTypeFile()) fileElem.parent().remove();
922
      angular.forEach(unwatches, function (unwatch) {
923
        unwatch();
924
      });
925
    });
926
927
    $timeout(function () {
928
      for (var i = 0; i < generatedElems.length; i++) {
929
        var g = generatedElems[i];
930
        if (!document.body.contains(g.el[0])) {
931
          generatedElems.splice(i, 1);
932
          g.ref.remove();
933
        }
934
      }
935
    });
936
937
    if (window.FileAPI && window.FileAPI.ngfFixIE) {
938
      window.FileAPI.ngfFixIE(elem, fileElem, changeFn);
939
    }
940
  }
941
942
  return {
943
    restrict: 'AEC',
944
    require: '?ngModel',
945
    link: function (scope, elem, attr, ngModel) {
946
      linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, Upload);
947
    }
948
  };
949
}]);
950
951 View Code Duplication
(function () {
952
953
  ngFileUpload.service('UploadDataUrl', ['UploadBase', '$timeout', '$q', function (UploadBase, $timeout, $q) {
954
    var upload = UploadBase;
955
    upload.base64DataUrl = function (file) {
956
      if (angular.isArray(file)) {
957
        var d = $q.defer(), count = 0;
958
        angular.forEach(file, function (f) {
959
          upload.dataUrl(f, true)['finally'](function () {
960
            count++;
961
            if (count === file.length) {
962
              var urls = [];
963
              angular.forEach(file, function (ff) {
964
                urls.push(ff.$ngfDataUrl);
965
              });
966
              d.resolve(urls, file);
967
            }
968
          });
969
        });
970
        return d.promise;
971
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
972
        return upload.dataUrl(file, true);
973
      }
974
    };
975
    upload.dataUrl = function (file, disallowObjectUrl) {
976
      if (!file) return upload.emptyPromise(file, file);
977
      if ((disallowObjectUrl && file.$ngfDataUrl != null) || (!disallowObjectUrl && file.$ngfBlobUrl != null)) {
978
        return upload.emptyPromise(disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl, file);
979
      }
980
      var p = disallowObjectUrl ? file.$$ngfDataUrlPromise : file.$$ngfBlobUrlPromise;
981
      if (p) return p;
982
983
      var deferred = $q.defer();
984
      $timeout(function () {
985
        if (window.FileReader && file &&
986
          (!window.FileAPI || navigator.userAgent.indexOf('MSIE 8') === -1 || file.size < 20000) &&
987
          (!window.FileAPI || navigator.userAgent.indexOf('MSIE 9') === -1 || file.size < 4000000)) {
988
          //prefer URL.createObjectURL for handling refrences to files of all sizes
989
          //since it doesn´t build a large string in memory
990
          var URL = window.URL || window.webkitURL;
991
          if (URL && URL.createObjectURL && !disallowObjectUrl) {
992
            var url;
993
            try {
994
              url = URL.createObjectURL(file);
995
            } catch (e) {
996
              $timeout(function () {
997
                file.$ngfBlobUrl = '';
998
                deferred.reject();
999
              });
1000
              return;
1001
            }
1002
            $timeout(function () {
1003
              file.$ngfBlobUrl = url;
1004
              if (url) {
1005
                deferred.resolve(url, file);
1006
                upload.blobUrls = upload.blobUrls || [];
1007
                upload.blobUrlsTotalSize = upload.blobUrlsTotalSize || 0;
1008
                upload.blobUrls.push({url: url, size: file.size});
1009
                upload.blobUrlsTotalSize += file.size || 0;
1010
                var maxMemory = upload.defaults.blobUrlsMaxMemory || 268435456;
1011
                var maxLength = upload.defaults.blobUrlsMaxQueueSize || 200;
1012
                while ((upload.blobUrlsTotalSize > maxMemory || upload.blobUrls.length > maxLength) && upload.blobUrls.length > 1) {
1013
                  var obj = upload.blobUrls.splice(0, 1)[0];
1014
                  URL.revokeObjectURL(obj.url);
1015
                  upload.blobUrlsTotalSize -= obj.size;
1016
                }
1017
              }
1018
            });
1019
          } else {
1020
            var fileReader = new FileReader();
1021
            fileReader.onload = function (e) {
1022
              $timeout(function () {
1023
                file.$ngfDataUrl = e.target.result;
1024
                deferred.resolve(e.target.result, file);
1025
                $timeout(function () {
1026
                  delete file.$ngfDataUrl;
1027
                }, 1000);
1028
              });
1029
            };
1030
            fileReader.onerror = function () {
1031
              $timeout(function () {
1032
                file.$ngfDataUrl = '';
1033
                deferred.reject();
1034
              });
1035
            };
1036
            fileReader.readAsDataURL(file);
1037
          }
1038
        } else {
1039
          $timeout(function () {
1040
            file[disallowObjectUrl ? '$ngfDataUrl' : '$ngfBlobUrl'] = '';
1041
            deferred.reject();
1042
          });
1043
        }
1044
      });
1045
1046
      if (disallowObjectUrl) {
1047
        p = file.$$ngfDataUrlPromise = deferred.promise;
1048
      } else {
1049
        p = file.$$ngfBlobUrlPromise = deferred.promise;
1050
      }
1051
      p['finally'](function () {
1052
        delete file[disallowObjectUrl ? '$$ngfDataUrlPromise' : '$$ngfBlobUrlPromise'];
1053
      });
1054
      return p;
1055
    };
1056
    return upload;
1057
  }]);
1058
1059
  function getTagType(el) {
1060
    if (el.tagName.toLowerCase() === 'img') return 'image';
1061
    if (el.tagName.toLowerCase() === 'audio') return 'audio';
1062
    if (el.tagName.toLowerCase() === 'video') return 'video';
1063
    return /./;
1064
  }
1065
1066
  function linkFileDirective(Upload, $timeout, scope, elem, attr, directiveName, resizeParams, isBackground) {
1067
    function constructDataUrl(file) {
1068
      var disallowObjectUrl = Upload.attrGetter('ngfNoObjectUrl', attr, scope);
1069
      Upload.dataUrl(file, disallowObjectUrl)['finally'](function () {
1070
        $timeout(function () {
1071
          var src = (disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl;
1072
          if (isBackground) {
1073
            elem.css('background-image', 'url(\'' + (src || '') + '\')');
1074
          } else {
1075
            elem.attr('src', src);
1076
          }
1077
          if (src) {
1078
            elem.removeClass('ng-hide');
1079
          } else {
1080
            elem.addClass('ng-hide');
1081
          }
1082
        });
1083
      });
1084
    }
1085
1086
    $timeout(function () {
1087
      var unwatch = scope.$watch(attr[directiveName], function (file) {
1088
        var size = resizeParams;
1089
        if (directiveName === 'ngfThumbnail') {
1090
          if (!size) {
1091
            size = {width: elem[0].naturalWidth || elem[0].clientWidth,
1092
              height: elem[0].naturalHeight || elem[0].clientHeight};
1093
          }
1094
          if (size.width === 0 && window.getComputedStyle) {
1095
            var style = getComputedStyle(elem[0]);
1096
            size = {
1097
              width: parseInt(style.width.slice(0, -2)),
1098
              height: parseInt(style.height.slice(0, -2))
1099
            };
1100
          }
1101
        }
1102
1103
        if (angular.isString(file)) {
1104
          elem.removeClass('ng-hide');
1105
          if (isBackground) {
1106
            return elem.css('background-image', 'url(\'' + file + '\')');
1107
          } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
1108
            return elem.attr('src', file);
1109
          }
1110
        }
1111
        if (file && file.type && file.type.search(getTagType(elem[0])) === 0 &&
1112
          (!isBackground || file.type.indexOf('image') === 0)) {
1113
          if (size && Upload.isResizeSupported()) {
1114
            size.resizeIf = function (width, height) {
1115
              return Upload.attrGetter('ngfResizeIf', attr, scope,
1116
                {$width: width, $height: height, $file: file});
1117
            };
1118
            Upload.resize(file, size).then(
1119
              function (f) {
1120
                constructDataUrl(f);
1121
              }, function (e) {
1122
                throw e;
1123
              }
1124
            );
1125
          } else {
1126
            constructDataUrl(file);
1127
          }
1128
        } else {
1129
          elem.addClass('ng-hide');
1130
        }
1131
      });
1132
1133
      scope.$on('$destroy', function () {
1134
        unwatch();
1135
      });
1136
    });
1137
  }
1138
1139
1140
  /** @namespace attr.ngfSrc */
1141
  /** @namespace attr.ngfNoObjectUrl */
1142
  ngFileUpload.directive('ngfSrc', ['Upload', '$timeout', function (Upload, $timeout) {
1143
    return {
1144
      restrict: 'AE',
1145
      link: function (scope, elem, attr) {
1146
        linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfSrc',
1147
          Upload.attrGetter('ngfResize', attr, scope), false);
1148
      }
1149
    };
1150
  }]);
1151
1152
  /** @namespace attr.ngfBackground */
1153
  /** @namespace attr.ngfNoObjectUrl */
1154
  ngFileUpload.directive('ngfBackground', ['Upload', '$timeout', function (Upload, $timeout) {
1155
    return {
1156
      restrict: 'AE',
1157
      link: function (scope, elem, attr) {
1158
        linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfBackground',
1159
          Upload.attrGetter('ngfResize', attr, scope), true);
1160
      }
1161
    };
1162
  }]);
1163
1164
  /** @namespace attr.ngfThumbnail */
1165
  /** @namespace attr.ngfAsBackground */
1166
  /** @namespace attr.ngfSize */
1167
  /** @namespace attr.ngfNoObjectUrl */
1168
  ngFileUpload.directive('ngfThumbnail', ['Upload', '$timeout', function (Upload, $timeout) {
1169
    return {
1170
      restrict: 'AE',
1171
      link: function (scope, elem, attr) {
1172
        var size = Upload.attrGetter('ngfSize', attr, scope);
1173
        linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfThumbnail', size,
1174
          Upload.attrGetter('ngfAsBackground', attr, scope));
1175
      }
1176
    };
1177
  }]);
1178
1179
  ngFileUpload.config(['$compileProvider', function ($compileProvider) {
1180
    if ($compileProvider.imgSrcSanitizationWhitelist) $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/);
1181
    if ($compileProvider.aHrefSanitizationWhitelist) $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/);
1182
  }]);
1183
1184
  ngFileUpload.filter('ngfDataUrl', ['UploadDataUrl', '$sce', function (UploadDataUrl, $sce) {
1185
    return function (file, disallowObjectUrl, trustedUrl) {
1186
      if (angular.isString(file)) {
1187
        return $sce.trustAsResourceUrl(file);
1188
      }
1189
      var src = file && ((disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl);
1190
      if (file && !src) {
1191
        if (!file.$ngfDataUrlFilterInProgress && angular.isObject(file)) {
1192
          file.$ngfDataUrlFilterInProgress = true;
1193
          UploadDataUrl.dataUrl(file, disallowObjectUrl);
1194
        }
1195
        return '';
1196
      }
1197
      if (file) delete file.$ngfDataUrlFilterInProgress;
1198
      return (file && src ? (trustedUrl ? $sce.trustAsResourceUrl(src) : src) : file) || '';
1199
    };
1200
  }]);
1201
1202
})();
1203
1204 View Code Duplication
ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) {
1205
  var upload = UploadDataUrl;
1206
1207
  function globStringToRegex(str) {
1208
    var regexp = '', excludes = [];
1209
    if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') {
1210
      regexp = str.substring(1, str.length - 1);
1211
    } else {
1212
      var split = str.split(',');
1213
      if (split.length > 1) {
1214
        for (var i = 0; i < split.length; i++) {
1215
          var r = globStringToRegex(split[i]);
1216
          if (r.regexp) {
1217
            regexp += '(' + r.regexp + ')';
1218
            if (i < split.length - 1) {
1219
              regexp += '|';
1220
            }
1221
          } else {
1222
            excludes = excludes.concat(r.excludes);
1223
          }
1224
        }
1225
      } else {
1226
        if (str.indexOf('!') === 0) {
1227
          excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$');
1228
        } else {
1229
          if (str.indexOf('.') === 0) {
1230
            str = '*' + str;
1231
          }
1232
          regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$';
1233
          regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
1234
        }
1235
      }
1236
    }
1237
    return {regexp: regexp, excludes: excludes};
1238
  }
1239
1240
  upload.validatePattern = function (file, val) {
1241
    if (!val) {
1242
      return true;
1243
    }
1244
    var pattern = globStringToRegex(val), valid = true;
1245
    if (pattern.regexp && pattern.regexp.length) {
1246
      var regexp = new RegExp(pattern.regexp, 'i');
1247
      valid = (file.type != null && regexp.test(file.type)) ||
1248
        (file.name != null && regexp.test(file.name));
1249
    }
1250
    var len = pattern.excludes.length;
1251
    while (len--) {
1252
      var exclude = new RegExp(pattern.excludes[len], 'i');
1253
      valid = valid && (file.type == null || exclude.test(file.type)) &&
1254
        (file.name == null || exclude.test(file.name));
1255
    }
1256
    return valid;
1257
  };
1258
1259
  upload.ratioToFloat = function (val) {
1260
    var r = val.toString(), xIndex = r.search(/[x:]/i);
1261
    if (xIndex > -1) {
1262
      r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1));
1263
    } else {
1264
      r = parseFloat(r);
1265
    }
1266
    return r;
1267
  };
1268
1269
  upload.registerModelChangeValidator = function (ngModel, attr, scope) {
1270
    if (ngModel) {
1271
      ngModel.$formatters.push(function (files) {
1272
        if (ngModel.$dirty) {
1273
          var filesArray = files;
1274
          if (files && !angular.isArray(files)) {
1275
            filesArray = [files];
1276
          }
1277
          upload.validate(filesArray, 0, ngModel, attr, scope).then(function () {
1278
            upload.applyModelValidation(ngModel, filesArray);
1279
          });
1280
        }
1281
        return files;
1282
      });
1283
    }
1284
  };
1285
1286
  function markModelAsDirty(ngModel, files) {
1287
    if (files != null && !ngModel.$dirty) {
1288
      if (ngModel.$setDirty) {
1289
        ngModel.$setDirty();
1290
      } else {
1291
        ngModel.$dirty = true;
1292
      }
1293
    }
1294
  }
1295
1296
  upload.applyModelValidation = function (ngModel, files) {
1297
    markModelAsDirty(ngModel, files);
1298
    angular.forEach(ngModel.$ngfValidations, function (validation) {
1299
      ngModel.$setValidity(validation.name, validation.valid);
1300
    });
1301
  };
1302
1303
  upload.getValidationAttr = function (attr, scope, name, validationName, file) {
1304
    var dName = 'ngf' + name[0].toUpperCase() + name.substr(1);
1305
    var val = upload.attrGetter(dName, attr, scope, {$file: file});
1306
    if (val == null) {
1307
      val = upload.attrGetter('ngfValidate', attr, scope, {$file: file});
1308
      if (val) {
1309
        var split = (validationName || name).split('.');
1310
        val = val[split[0]];
1311
        if (split.length > 1) {
1312
          val = val && val[split[1]];
1313
        }
1314
      }
1315
    }
1316
    return val;
1317
  };
1318
1319
  upload.validate = function (files, prevLength, ngModel, attr, scope) {
1320
    ngModel = ngModel || {};
1321
    ngModel.$ngfValidations = ngModel.$ngfValidations || [];
1322
1323
    angular.forEach(ngModel.$ngfValidations, function (v) {
1324
      v.valid = true;
1325
    });
1326
1327
    var attrGetter = function (name, params) {
1328
      return upload.attrGetter(name, attr, scope, params);
1329
    };
1330
1331
    var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' ');
1332
    var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope);
1333
1334
    if (files == null || files.length === 0) {
1335
      return upload.emptyPromise({'validFiles': files, 'invalidFiles': []});
1336
    }
1337
1338
    files = files.length === undefined ? [files] : files.slice(0);
1339
    var invalidFiles = [];
1340
1341
    function validateSync(name, validationName, fn) {
1342
      if (files) {
1343
        var i = files.length, valid = null;
1344
        while (i--) {
1345
          var file = files[i];
1346
          if (file) {
1347
            var val = upload.getValidationAttr(attr, scope, name, validationName, file);
1348
            if (val != null) {
1349
              if (!fn(file, val, i)) {
1350
                if (ignoredErrors.indexOf(name) === -1) {
1351
                  file.$error = name;
1352
                  (file.$errorMessages = (file.$errorMessages || {}))[name] = true;
1353
                  file.$errorParam = val;
1354
                  if (invalidFiles.indexOf(file) === -1) {
1355
                    invalidFiles.push(file);
1356
                  }
1357
                  if (!runAllValidation) {
1358
                    files.splice(i, 1);
1359
                  }
1360
                  valid = false;
1361
                } else {
1362
                  files.splice(i, 1);
1363
                }
1364
              }
1365
            }
1366
          }
1367
        }
1368
        if (valid !== null) {
1369
          ngModel.$ngfValidations.push({name: name, valid: valid});
1370
        }
1371
      }
1372
    }
1373
1374
    validateSync('pattern', null, upload.validatePattern);
1375
    validateSync('minSize', 'size.min', function (file, val) {
1376
      return file.size + 0.1 >= upload.translateScalars(val);
1377
    });
1378
    validateSync('maxSize', 'size.max', function (file, val) {
1379
      return file.size - 0.1 <= upload.translateScalars(val);
1380
    });
1381
    var totalSize = 0;
1382
    validateSync('maxTotalSize', null, function (file, val) {
1383
      totalSize += file.size;
1384
      if (totalSize > upload.translateScalars(val)) {
1385
        files.splice(0, files.length);
1386
        return false;
1387
      }
1388
      return true;
1389
    });
1390
1391
    validateSync('validateFn', null, function (file, r) {
1392
      return r === true || r === null || r === '';
1393
    });
1394
1395
    if (!files.length) {
1396
      return upload.emptyPromise({'validFiles': [], 'invalidFiles': invalidFiles});
1397
    }
1398
1399
    function validateAsync(name, validationName, type, asyncFn, fn) {
1400
      function resolveResult(defer, file, val) {
1401
        function resolveInternal(fn) {
1402
          if (fn()) {
1403
            if (ignoredErrors.indexOf(name) === -1) {
1404
              file.$error = name;
1405
              (file.$errorMessages = (file.$errorMessages || {}))[name] = true;
1406
              file.$errorParam = val;
1407
              if (invalidFiles.indexOf(file) === -1) {
1408
                invalidFiles.push(file);
1409
              }
1410
              if (!runAllValidation) {
1411
                var i = files.indexOf(file);
1412
                if (i > -1) files.splice(i, 1);
1413
              }
1414
              defer.resolve(false);
1415
            } else {
1416
              var j = files.indexOf(file);
1417
              if (j > -1) files.splice(j, 1);
1418
              defer.resolve(true);
1419
            }
1420
          } else {
1421
            defer.resolve(true);
1422
          }
1423
        }
1424
1425
        if (val != null) {
1426
          asyncFn(file, val).then(function (d) {
1427
            resolveInternal(function () {
1428
              return !fn(d, val);
1429
            });
1430
          }, function () {
1431
            resolveInternal(function () {
1432
              return attrGetter('ngfValidateForce', {$file: file});
1433
            });
1434
          });
1435
        } else {
1436
          defer.resolve(true);
1437
        }
1438
      }
1439
1440
      var promises = [upload.emptyPromise(true)];
1441
      if (files) {
1442
        files = files.length === undefined ? [files] : files;
1443
        angular.forEach(files, function (file) {
1444
          var defer = $q.defer();
1445
          promises.push(defer.promise);
1446
          if (type && (file.type == null || file.type.search(type) !== 0)) {
1447
            defer.resolve(true);
1448
            return;
1449
          }
1450
          if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) {
1451
            upload.imageDimensions(file).then(function (d) {
1452
              resolveResult(defer, file,
1453
                attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height}));
1454
            }, function () {
1455
              defer.resolve(false);
1456
            });
1457
          } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) {
1458
            upload.mediaDuration(file).then(function (d) {
1459
              resolveResult(defer, file,
1460
                attrGetter('ngfDuration', {$file: file, $duration: d}));
1461
            }, function () {
1462
              defer.resolve(false);
1463
            });
1464
          } else {
1465
            resolveResult(defer, file,
1466
              upload.getValidationAttr(attr, scope, name, validationName, file));
1467
          }
1468
        });
1469
      }
1470
      var deffer = $q.defer();
1471
      $q.all(promises).then(function (values) {
1472
        var isValid = true;
1473
        for (var i = 0; i < values.length; i++) {
1474
          if (!values[i]) {
1475
            isValid = false;
1476
            break;
1477
          }
1478
        }
1479
        ngModel.$ngfValidations.push({name: name, valid: isValid});
1480
        deffer.resolve(isValid);
1481
      });
1482
      return deffer.promise;
1483
    }
1484
1485
    var deffer = $q.defer();
1486
    var promises = [];
1487
1488
    promises.push(validateAsync('maxHeight', 'height.max', /image/,
1489
      this.imageDimensions, function (d, val) {
1490
        return d.height <= val;
1491
      }));
1492
    promises.push(validateAsync('minHeight', 'height.min', /image/,
1493
      this.imageDimensions, function (d, val) {
1494
        return d.height >= val;
1495
      }));
1496
    promises.push(validateAsync('maxWidth', 'width.max', /image/,
1497
      this.imageDimensions, function (d, val) {
1498
        return d.width <= val;
1499
      }));
1500
    promises.push(validateAsync('minWidth', 'width.min', /image/,
1501
      this.imageDimensions, function (d, val) {
1502
        return d.width >= val;
1503
      }));
1504
    promises.push(validateAsync('dimensions', null, /image/,
1505
      function (file, val) {
1506
        return upload.emptyPromise(val);
1507
      }, function (r) {
1508
        return r;
1509
      }));
1510
    promises.push(validateAsync('ratio', null, /image/,
1511
      this.imageDimensions, function (d, val) {
1512
        var split = val.toString().split(','), valid = false;
1513
        for (var i = 0; i < split.length; i++) {
1514
          if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) {
1515
            valid = true;
1516
          }
1517
        }
1518
        return valid;
1519
      }));
1520
    promises.push(validateAsync('maxRatio', 'ratio.max', /image/,
1521
      this.imageDimensions, function (d, val) {
1522
        return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001;
1523
      }));
1524
    promises.push(validateAsync('minRatio', 'ratio.min', /image/,
1525
      this.imageDimensions, function (d, val) {
1526
        return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001;
1527
      }));
1528
    promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/,
1529
      this.mediaDuration, function (d, val) {
1530
        return d <= upload.translateScalars(val);
1531
      }));
1532
    promises.push(validateAsync('minDuration', 'duration.min', /audio|video/,
1533
      this.mediaDuration, function (d, val) {
1534
        return d >= upload.translateScalars(val);
1535
      }));
1536
    promises.push(validateAsync('duration', null, /audio|video/,
1537
      function (file, val) {
1538
        return upload.emptyPromise(val);
1539
      }, function (r) {
1540
        return r;
1541
      }));
1542
1543
    promises.push(validateAsync('validateAsyncFn', null, null,
1544
      function (file, val) {
1545
        return val;
1546
      }, function (r) {
1547
        return r === true || r === null || r === '';
1548
      }));
1549
1550
    $q.all(promises).then(function () {
1551
1552
      if (runAllValidation) {
1553
        for (var i = 0; i < files.length; i++) {
1554
          var file = files[i];
1555
          if (file.$error) {
1556
            files.splice(i--, 1);
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
1557
          }
1558
        }
1559
      }
1560
1561
      runAllValidation = false;
1562
      validateSync('maxFiles', null, function (file, val, i) {
1563
        return prevLength + i < val;
1564
      });
1565
1566
      deffer.resolve({'validFiles': files, 'invalidFiles': invalidFiles});
1567
    });
1568
    return deffer.promise;
1569
  };
1570
1571
  upload.imageDimensions = function (file) {
1572
    if (file.$ngfWidth && file.$ngfHeight) {
1573
      var d = $q.defer();
1574
      $timeout(function () {
1575
        d.resolve({width: file.$ngfWidth, height: file.$ngfHeight});
1576
      });
1577
      return d.promise;
1578
    }
1579
    if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise;
1580
1581
    var deferred = $q.defer();
1582
    $timeout(function () {
1583
      if (file.type.indexOf('image') !== 0) {
1584
        deferred.reject('not image');
1585
        return;
1586
      }
1587
      upload.dataUrl(file).then(function (dataUrl) {
1588
        var img = angular.element('<img>').attr('src', dataUrl)
1589
          .css('visibility', 'hidden').css('position', 'fixed')
1590
          .css('max-width', 'none !important').css('max-height', 'none !important');
1591
1592
        function success() {
1593
          var width = img[0].naturalWidth || img[0].clientWidth;
1594
          var height = img[0].naturalHeight || img[0].clientHeight;
1595
          img.remove();
1596
          file.$ngfWidth = width;
1597
          file.$ngfHeight = height;
1598
          deferred.resolve({width: width, height: height});
1599
        }
1600
1601
        function error() {
1602
          img.remove();
1603
          deferred.reject('load error');
1604
        }
1605
1606
        img.on('load', success);
1607
        img.on('error', error);
1608
1609
        var secondsCounter = 0;
1610
        function checkLoadErrorInCaseOfNoCallback() {
1611
          $timeout(function () {
1612
            if (img[0].parentNode) {
1613
              if (img[0].clientWidth) {
1614
                success();
1615
              } else if (secondsCounter++ > 10) {
1616
                error();
1617
              } else {
1618
                checkLoadErrorInCaseOfNoCallback();
1619
              }
1620
            }
1621
          }, 1000);
1622
        }
1623
1624
        checkLoadErrorInCaseOfNoCallback();
1625
1626
        angular.element(document.getElementsByTagName('body')[0]).append(img);
1627
      }, function () {
1628
        deferred.reject('load error');
1629
      });
1630
    });
1631
1632
    file.$ngfDimensionPromise = deferred.promise;
1633
    file.$ngfDimensionPromise['finally'](function () {
1634
      delete file.$ngfDimensionPromise;
1635
    });
1636
    return file.$ngfDimensionPromise;
1637
  };
1638
1639
  upload.mediaDuration = function (file) {
1640
    if (file.$ngfDuration) {
1641
      var d = $q.defer();
1642
      $timeout(function () {
1643
        d.resolve(file.$ngfDuration);
1644
      });
1645
      return d.promise;
1646
    }
1647
    if (file.$ngfDurationPromise) return file.$ngfDurationPromise;
1648
1649
    var deferred = $q.defer();
1650
    $timeout(function () {
1651
      if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) {
1652
        deferred.reject('not media');
1653
        return;
1654
      }
1655
      upload.dataUrl(file).then(function (dataUrl) {
1656
        var el = angular.element(file.type.indexOf('audio') === 0 ? '<audio>' : '<video>')
1657
          .attr('src', dataUrl).css('visibility', 'none').css('position', 'fixed');
1658
1659
        function success() {
1660
          var duration = el[0].duration;
1661
          file.$ngfDuration = duration;
1662
          el.remove();
1663
          deferred.resolve(duration);
1664
        }
1665
1666
        function error() {
1667
          el.remove();
1668
          deferred.reject('load error');
1669
        }
1670
1671
        el.on('loadedmetadata', success);
1672
        el.on('error', error);
1673
        var count = 0;
1674
1675
        function checkLoadError() {
1676
          $timeout(function () {
1677
            if (el[0].parentNode) {
1678
              if (el[0].duration) {
1679
                success();
1680
              } else if (count > 10) {
1681
                error();
1682
              } else {
1683
                checkLoadError();
1684
              }
1685
            }
1686
          }, 1000);
1687
        }
1688
1689
        checkLoadError();
1690
1691
        angular.element(document.body).append(el);
1692
      }, function () {
1693
        deferred.reject('load error');
1694
      });
1695
    });
1696
1697
    file.$ngfDurationPromise = deferred.promise;
1698
    file.$ngfDurationPromise['finally'](function () {
1699
      delete file.$ngfDurationPromise;
1700
    });
1701
    return file.$ngfDurationPromise;
1702
  };
1703
  return upload;
1704
}
1705
]);
1706
1707 View Code Duplication
ngFileUpload.service('UploadResize', ['UploadValidate', '$q', function (UploadValidate, $q) {
1708
  var upload = UploadValidate;
1709
1710
  /**
1711
   * Conserve aspect ratio of the original region. Useful when shrinking/enlarging
1712
   * images to fit into a certain area.
1713
   * Source:  http://stackoverflow.com/a/14731922
1714
   *
1715
   * @param {Number} srcWidth Source area width
1716
   * @param {Number} srcHeight Source area height
1717
   * @param {Number} maxWidth Nestable area maximum available width
1718
   * @param {Number} maxHeight Nestable area maximum available height
1719
   * @return {Object} { width, height }
1720
   */
1721
  var calculateAspectRatioFit = function (srcWidth, srcHeight, maxWidth, maxHeight, centerCrop) {
1722
    var ratio = centerCrop ? Math.max(maxWidth / srcWidth, maxHeight / srcHeight) :
1723
      Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
1724
    return {
1725
      width: srcWidth * ratio, height: srcHeight * ratio,
1726
      marginX: srcWidth * ratio - maxWidth, marginY: srcHeight * ratio - maxHeight
1727
    };
1728
  };
1729
1730
  // Extracted from https://github.com/romelgomez/angular-firebase-image-upload/blob/master/app/scripts/fileUpload.js#L89
1731
  var resize = function (imagen, width, height, quality, type, ratio, centerCrop, resizeIf) {
1732
    var deferred = $q.defer();
1733
    var canvasElement = document.createElement('canvas');
1734
    var imageElement = document.createElement('img');
1735
    imageElement.setAttribute('style', 'visibility:hidden;position:fixed;z-index:-100000');
1736
    document.body.appendChild(imageElement);
1737
1738
    imageElement.onload = function () {
1739
      var imgWidth = imageElement.width, imgHeight = imageElement.height;
1740
      imageElement.parentNode.removeChild(imageElement);
1741
      if (resizeIf != null && resizeIf(imgWidth, imgHeight) === false) {
1742
        deferred.reject('resizeIf');
1743
        return;
1744
      }
1745
      try {
1746
        if (ratio) {
1747
          var ratioFloat = upload.ratioToFloat(ratio);
1748
          var imgRatio = imgWidth / imgHeight;
1749
          if (imgRatio < ratioFloat) {
1750
            width = imgWidth;
1751
            height = width / ratioFloat;
1752
          } else {
1753
            height = imgHeight;
1754
            width = height * ratioFloat;
1755
          }
1756
        }
1757
        if (!width) {
1758
          width = imgWidth;
1759
        }
1760
        if (!height) {
1761
          height = imgHeight;
1762
        }
1763
        var dimensions = calculateAspectRatioFit(imgWidth, imgHeight, width, height, centerCrop);
1764
        canvasElement.width = Math.min(dimensions.width, width);
1765
        canvasElement.height = Math.min(dimensions.height, height);
1766
        var context = canvasElement.getContext('2d');
1767
        context.drawImage(imageElement,
1768
          Math.min(0, -dimensions.marginX / 2), Math.min(0, -dimensions.marginY / 2),
1769
          dimensions.width, dimensions.height);
1770
        deferred.resolve(canvasElement.toDataURL(type || 'image/WebP', quality || 0.934));
1771
      } catch (e) {
1772
        deferred.reject(e);
1773
      }
1774
    };
1775
    imageElement.onerror = function () {
1776
      imageElement.parentNode.removeChild(imageElement);
1777
      deferred.reject();
1778
    };
1779
    imageElement.src = imagen;
1780
    return deferred.promise;
1781
  };
1782
1783
  upload.dataUrltoBlob = function (dataurl, name, origSize) {
1784
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
1785
      bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
1786
    while (n--) {
1787
      u8arr[n] = bstr.charCodeAt(n);
1788
    }
1789
    var blob = new window.Blob([u8arr], {type: mime});
1790
    blob.name = name;
1791
    blob.$ngfOrigSize = origSize;
1792
    return blob;
1793
  };
1794
1795
  upload.isResizeSupported = function () {
1796
    var elem = document.createElement('canvas');
1797
    return window.atob && elem.getContext && elem.getContext('2d') && window.Blob;
1798
  };
1799
1800
  if (upload.isResizeSupported()) {
1801
    // add name getter to the blob constructor prototype
1802
    Object.defineProperty(window.Blob.prototype, 'name', {
1803
      get: function () {
1804
        return this.$ngfName;
1805
      },
1806
      set: function (v) {
1807
        this.$ngfName = v;
1808
      },
1809
      configurable: true
1810
    });
1811
  }
1812
1813
  upload.resize = function (file, options) {
1814
    if (file.type.indexOf('image') !== 0) return upload.emptyPromise(file);
1815
1816
    var deferred = $q.defer();
1817
    upload.dataUrl(file, true).then(function (url) {
1818
      resize(url, options.width, options.height, options.quality, options.type || file.type,
1819
        options.ratio, options.centerCrop, options.resizeIf)
1820
        .then(function (dataUrl) {
1821
          if (file.type === 'image/jpeg' && options.restoreExif !== false) {
1822
            try {
1823
              dataUrl = upload.restoreExif(url, dataUrl);
1824
            } catch (e) {
1825
              setTimeout(function () {throw e;}, 1);
1826
            }
1827
          }
1828
          try {
1829
            var blob = upload.dataUrltoBlob(dataUrl, file.name, file.size);
1830
            deferred.resolve(blob);
1831
          } catch (e) {
1832
            deferred.reject(e);
1833
          }
1834
        }, function (r) {
1835
          if (r === 'resizeIf') {
1836
            deferred.resolve(file);
1837
          }
1838
          deferred.reject(r);
1839
        });
1840
    }, function (e) {
1841
      deferred.reject(e);
1842
    });
1843
    return deferred.promise;
1844
  };
1845
1846
  return upload;
1847
}]);
1848
1849 View Code Duplication
(function () {
1850
  ngFileUpload.directive('ngfDrop', ['$parse', '$timeout', '$window', 'Upload', '$http', '$q',
1851
    function ($parse, $timeout, $window, Upload, $http, $q) {
1852
      return {
1853
        restrict: 'AEC',
1854
        require: '?ngModel',
1855
        link: function (scope, elem, attr, ngModel) {
1856
          linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window, Upload, $http, $q);
1857
        }
1858
      };
1859
    }]);
1860
1861
  ngFileUpload.directive('ngfNoFileDrop', function () {
1862
    return function (scope, elem) {
1863
      if (dropAvailable()) elem.css('display', 'none');
1864
    };
1865
  });
1866
1867
  ngFileUpload.directive('ngfDropAvailable', ['$parse', '$timeout', 'Upload', function ($parse, $timeout, Upload) {
1868
    return function (scope, elem, attr) {
1869
      if (dropAvailable()) {
1870
        var model = $parse(Upload.attrGetter('ngfDropAvailable', attr));
1871
        $timeout(function () {
1872
          model(scope);
1873
          if (model.assign) {
1874
            model.assign(scope, true);
1875
          }
1876
        });
1877
      }
1878
    };
1879
  }]);
1880
1881
  function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window, upload, $http, $q) {
1882
    var available = dropAvailable();
1883
1884
    var attrGetter = function (name, scope, params) {
1885
      return upload.attrGetter(name, attr, scope, params);
1886
    };
1887
1888
    if (attrGetter('dropAvailable')) {
1889
      $timeout(function () {
1890
        if (scope[attrGetter('dropAvailable')]) {
1891
          scope[attrGetter('dropAvailable')].value = available;
1892
        } else {
1893
          scope[attrGetter('dropAvailable')] = available;
1894
        }
1895
      });
1896
    }
1897
    if (!available) {
1898
      if (attrGetter('ngfHideOnDropNotAvailable', scope) === true) {
1899
        elem.css('display', 'none');
1900
      }
1901
      return;
1902
    }
1903
1904
    function isDisabled() {
1905
      return elem.attr('disabled') || attrGetter('ngfDropDisabled', scope);
1906
    }
1907
1908
    if (attrGetter('ngfSelect') == null) {
1909
      upload.registerModelChangeValidator(ngModel, attr, scope);
1910
    }
1911
1912
    var leaveTimeout = null;
1913
    var stopPropagation = $parse(attrGetter('ngfStopPropagation'));
1914
    var dragOverDelay = 1;
1915
    var actualDragOverClass;
1916
1917
    elem[0].addEventListener('dragover', function (evt) {
1918
      if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return;
1919
      evt.preventDefault();
1920
      if (stopPropagation(scope)) evt.stopPropagation();
1921
      // handling dragover events from the Chrome download bar
1922
      if (navigator.userAgent.indexOf('Chrome') > -1) {
1923
        var b = evt.dataTransfer.effectAllowed;
1924
        evt.dataTransfer.dropEffect = ('move' === b || 'linkMove' === b) ? 'move' : 'copy';
1925
      }
1926
      $timeout.cancel(leaveTimeout);
1927
      if (!actualDragOverClass) {
1928
        actualDragOverClass = 'C';
1929
        calculateDragOverClass(scope, attr, evt, function (clazz) {
1930
          actualDragOverClass = clazz;
1931
          elem.addClass(actualDragOverClass);
1932
          attrGetter('ngfDrag', scope, {$isDragging: true, $class: actualDragOverClass, $event: evt});
1933
        });
1934
      }
1935
    }, false);
1936
    elem[0].addEventListener('dragenter', function (evt) {
1937
      if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return;
1938
      evt.preventDefault();
1939
      if (stopPropagation(scope)) evt.stopPropagation();
1940
    }, false);
1941
    elem[0].addEventListener('dragleave', function (evt) {
1942
      if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return;
1943
      evt.preventDefault();
1944
      if (stopPropagation(scope)) evt.stopPropagation();
1945
      leaveTimeout = $timeout(function () {
1946
        if (actualDragOverClass) elem.removeClass(actualDragOverClass);
1947
        actualDragOverClass = null;
1948
        attrGetter('ngfDrag', scope, {$isDragging: false, $event: evt});
1949
      }, dragOverDelay || 100);
1950
    }, false);
1951
    elem[0].addEventListener('drop', function (evt) {
1952
      if (isDisabled() || !upload.shouldUpdateOn('drop', attr, scope)) return;
1953
      evt.preventDefault();
1954
      if (stopPropagation(scope)) evt.stopPropagation();
1955
      if (actualDragOverClass) elem.removeClass(actualDragOverClass);
1956
      actualDragOverClass = null;
1957
      var items = evt.dataTransfer.items;
1958
      var html;
1959
      try {
1960
        html = evt.dataTransfer && evt.dataTransfer.getData && evt.dataTransfer.getData('text/html');
1961
      } catch (e) {/* Fix IE11 that throw error calling getData */
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
1962
      }
1963
1964
      extractFiles(items, evt.dataTransfer.files, attrGetter('ngfAllowDir', scope) !== false,
1965
        attrGetter('multiple') || attrGetter('ngfMultiple', scope)).then(function (files) {
1966
        if (files.length) {
1967
          updateModel(files, evt);
1968
        } else {
1969
          extractFilesFromHtml('dropUrl', html).then(function (files) {
1970
            updateModel(files, evt);
1971
          });
1972
        }
1973
      });
1974
    }, false);
1975
    elem[0].addEventListener('paste', function (evt) {
1976
      if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 &&
1977
        attrGetter('ngfEnableFirefoxPaste', scope)) {
1978
        evt.preventDefault();
1979
      }
1980
      if (isDisabled() || !upload.shouldUpdateOn('paste', attr, scope)) return;
1981
      var files = [];
1982
      var clipboard = evt.clipboardData || evt.originalEvent.clipboardData;
1983
      if (clipboard && clipboard.items) {
1984
        for (var k = 0; k < clipboard.items.length; k++) {
1985
          if (clipboard.items[k].type.indexOf('image') !== -1) {
1986
            files.push(clipboard.items[k].getAsFile());
1987
          }
1988
        }
1989
      }
1990
      if (files.length) {
1991
        updateModel(files, evt);
1992
      } else {
1993
        extractFilesFromHtml('pasteUrl', clipboard).then(function (files) {
1994
          updateModel(files, evt);
1995
        });
1996
      }
1997
    }, false);
1998
1999
    if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 &&
2000
      attrGetter('ngfEnableFirefoxPaste', scope)) {
2001
      elem.attr('contenteditable', true);
2002
      elem.on('keypress', function (e) {
2003
        if (!e.metaKey && !e.ctrlKey) {
2004
          e.preventDefault();
2005
        }
2006
      });
2007
    }
2008
2009
    function updateModel(files, evt) {
2010
      upload.updateModel(ngModel, attr, scope, attrGetter('ngfChange') || attrGetter('ngfDrop'), files, evt);
2011
    }
2012
2013
    function extractFilesFromHtml(updateOn, html) {
2014
      if (!upload.shouldUpdateOn(updateOn, attr, scope) || typeof html !== 'string') return upload.rejectPromise([]);
2015
      var urls = [];
2016
      html.replace(/<(img src|img [^>]* src) *=\"([^\"]*)\"/gi, function (m, n, src) {
2017
        urls.push(src);
2018
      });
2019
      var promises = [], files = [];
2020
      if (urls.length) {
2021
        angular.forEach(urls, function (url) {
2022
          promises.push(upload.urlToBlob(url).then(function (blob) {
2023
            files.push(blob);
2024
          }));
2025
        });
2026
        var defer = $q.defer();
2027
        $q.all(promises).then(function () {
2028
          defer.resolve(files);
2029
        }, function (e) {
2030
          defer.reject(e);
2031
        });
2032
        return defer.promise;
2033
      }
2034
      return upload.emptyPromise();
2035
    }
2036
2037
    function calculateDragOverClass(scope, attr, evt, callback) {
2038
      var obj = attrGetter('ngfDragOverClass', scope, {$event: evt}), dClass = 'dragover';
2039
      if (angular.isString(obj)) {
2040
        dClass = obj;
2041
      } else if (obj) {
2042
        if (obj.delay) dragOverDelay = obj.delay;
2043
        if (obj.accept || obj.reject) {
2044
          var items = evt.dataTransfer.items;
2045
          if (items == null || !items.length) {
2046
            dClass = obj.accept;
2047
          } else {
2048
            var pattern = obj.pattern || attrGetter('ngfPattern', scope, {$event: evt});
2049
            var len = items.length;
2050
            while (len--) {
2051
              if (!upload.validatePattern(items[len], pattern)) {
2052
                dClass = obj.reject;
2053
                break;
2054
              } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2055
                dClass = obj.accept;
2056
              }
2057
            }
2058
          }
2059
        }
2060
      }
2061
      callback(dClass);
2062
    }
2063
2064
    function extractFiles(items, fileList, allowDir, multiple) {
2065
      var maxFiles = upload.getValidationAttr(attr, scope, 'maxFiles');
2066
      if (maxFiles == null) {
2067
        maxFiles = Number.MAX_VALUE;
2068
      }
2069
      var maxTotalSize = upload.getValidationAttr(attr, scope, 'maxTotalSize');
2070
      if (maxTotalSize == null) {
2071
        maxTotalSize = Number.MAX_VALUE;
2072
      }
2073
      var includeDir = attrGetter('ngfIncludeDir', scope);
2074
      var files = [], totalSize = 0;
2075
2076
      function traverseFileTree(entry, path) {
2077
        var defer = $q.defer();
2078
        if (entry != null) {
2079
          if (entry.isDirectory) {
2080
            var promises = [upload.emptyPromise()];
2081
            if (includeDir) {
2082
              var file = {type: 'directory'};
2083
              file.name = file.path = (path || '') + entry.name;
2084
              files.push(file);
2085
            }
2086
            var dirReader = entry.createReader();
2087
            var entries = [];
2088
            var readEntries = function () {
2089
              dirReader.readEntries(function (results) {
2090
                try {
2091
                  if (!results.length) {
2092
                    angular.forEach(entries.slice(0), function (e) {
2093
                      if (files.length <= maxFiles && totalSize <= maxTotalSize) {
2094
                        promises.push(traverseFileTree(e, (path ? path : '') + entry.name + '/'));
2095
                      }
2096
                    });
2097
                    $q.all(promises).then(function () {
2098
                      defer.resolve();
2099
                    }, function (e) {
2100
                      defer.reject(e);
2101
                    });
2102
                  } else {
2103
                    entries = entries.concat(Array.prototype.slice.call(results || [], 0));
2104
                    readEntries();
2105
                  }
2106
                } catch (e) {
2107
                  defer.reject(e);
2108
                }
2109
              }, function (e) {
2110
                defer.reject(e);
2111
              });
2112
            };
2113
            readEntries();
2114
          } else {
2115
            entry.file(function (file) {
2116
              try {
2117
                file.path = (path ? path : '') + file.name;
2118
                if (includeDir) {
2119
                  file = upload.rename(file, file.path);
2120
                }
2121
                files.push(file);
2122
                totalSize += file.size;
2123
                defer.resolve();
2124
              } catch (e) {
2125
                defer.reject(e);
2126
              }
2127
            }, function (e) {
2128
              defer.reject(e);
2129
            });
2130
          }
2131
        }
2132
        return defer.promise;
2133
      }
2134
2135
      var promises = [upload.emptyPromise()];
2136
2137
      if (items && items.length > 0 && $window.location.protocol !== 'file:') {
2138
        for (var i = 0; i < items.length; i++) {
2139
          if (items[i].webkitGetAsEntry && items[i].webkitGetAsEntry() && items[i].webkitGetAsEntry().isDirectory) {
2140
            var entry = items[i].webkitGetAsEntry();
2141
            if (entry.isDirectory && !allowDir) {
2142
              continue;
2143
            }
2144
            if (entry != null) {
2145
              promises.push(traverseFileTree(entry));
2146
            }
2147
          } else {
2148
            var f = items[i].getAsFile();
2149
            if (f != null) {
2150
              files.push(f);
2151
              totalSize += f.size;
2152
            }
2153
          }
2154
          if (files.length > maxFiles || totalSize > maxTotalSize ||
2155
            (!multiple && files.length > 0)) break;
2156
        }
2157
      } else {
2158
        if (fileList != null) {
2159
          for (var j = 0; j < fileList.length; j++) {
2160
            var file = fileList.item(j);
2161
            if (file.type || file.size > 0) {
2162
              files.push(file);
2163
              totalSize += file.size;
2164
            }
2165
            if (files.length > maxFiles || totalSize > maxTotalSize ||
2166
              (!multiple && files.length > 0)) break;
2167
          }
2168
        }
2169
      }
2170
2171
      var defer = $q.defer();
2172
      $q.all(promises).then(function () {
2173
        if (!multiple && !includeDir && files.length) {
2174
          var i = 0;
2175
          while (files[i] && files[i].type === 'directory') i++;
2176
          defer.resolve([files[i]]);
2177
        } else {
2178
          defer.resolve(files);
2179
        }
2180
      }, function (e) {
2181
        defer.reject(e);
2182
      });
2183
2184
      return defer.promise;
2185
    }
2186
  }
2187
2188
  function dropAvailable() {
2189
    var div = document.createElement('div');
2190
    return ('draggable' in div) && ('ondrop' in div) && !/Edge\/12./i.test(navigator.userAgent);
2191
  }
2192
2193
})();
2194
2195
// customized version of https://github.com/exif-js/exif-js
2196 View Code Duplication
ngFileUpload.service('UploadExif', ['UploadResize', '$q', function (UploadResize, $q) {
2197
  var upload = UploadResize;
2198
2199
  upload.isExifSupported = function () {
2200
    return window.FileReader && new FileReader().readAsArrayBuffer && upload.isResizeSupported();
2201
  };
2202
2203
  function applyTransform(ctx, orientation, width, height) {
2204
    switch (orientation) {
2205
      case 2:
2206
        return ctx.transform(-1, 0, 0, 1, width, 0);
2207
      case 3:
2208
        return ctx.transform(-1, 0, 0, -1, width, height);
2209
      case 4:
2210
        return ctx.transform(1, 0, 0, -1, 0, height);
2211
      case 5:
2212
        return ctx.transform(0, 1, 1, 0, 0, 0);
2213
      case 6:
2214
        return ctx.transform(0, 1, -1, 0, height, 0);
2215
      case 7:
2216
        return ctx.transform(0, -1, -1, 0, height, width);
2217
      case 8:
2218
        return ctx.transform(0, -1, 1, 0, 0, width);
2219
    }
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
2220
  }
2221
2222
  upload.readOrientation = function (file) {
2223
    var defer = $q.defer();
2224
    var reader = new FileReader();
2225
    var slicedFile = file.slice ? file.slice(0, 64 * 1024) : file;
2226
    reader.readAsArrayBuffer(slicedFile);
2227
    reader.onerror = function (e) {
2228
      return defer.reject(e);
2229
    };
2230
    reader.onload = function (e) {
2231
      var result = {orientation: 1};
2232
      var view = new DataView(this.result);
2233
      if (view.getUint16(0, false) !== 0xFFD8) return defer.resolve(result);
2234
2235
      var length = view.byteLength,
2236
        offset = 2;
2237
      while (offset < length) {
2238
        var marker = view.getUint16(offset, false);
2239
        offset += 2;
2240
        if (marker === 0xFFE1) {
2241
          if (view.getUint32(offset += 2, false) !== 0x45786966) return defer.resolve(result);
2242
2243
          var little = view.getUint16(offset += 6, false) === 0x4949;
2244
          offset += view.getUint32(offset + 4, little);
2245
          var tags = view.getUint16(offset, little);
2246
          offset += 2;
2247
          for (var i = 0; i < tags; i++)
2248
            if (view.getUint16(offset + (i * 12), little) === 0x0112) {
2249
              var orientation = view.getUint16(offset + (i * 12) + 8, little);
2250
              if (orientation >= 2 && orientation <= 8) {
2251
                view.setUint16(offset + (i * 12) + 8, 1, little);
2252
                result.fixedArrayBuffer = e.target.result;
2253
              }
2254
              result.orientation = orientation;
2255
              return defer.resolve(result);
2256
            }
2257
        } else if ((marker & 0xFF00) !== 0xFF00) break;
2258
        else offset += view.getUint16(offset, false);
2259
      }
2260
      return defer.resolve(result);
2261
    };
2262
    return defer.promise;
2263
  };
2264
2265
  function arrayBufferToBase64(buffer) {
2266
    var binary = '';
2267
    var bytes = new Uint8Array(buffer);
2268
    var len = bytes.byteLength;
2269
    for (var i = 0; i < len; i++) {
2270
      binary += String.fromCharCode(bytes[i]);
2271
    }
2272
    return window.btoa(binary);
2273
  }
2274
2275
  upload.applyExifRotation = function (file) {
2276
    if (file.type.indexOf('image/jpeg') !== 0) {
2277
      return upload.emptyPromise(file);
2278
    }
2279
2280
    var deferred = $q.defer();
2281
    upload.readOrientation(file).then(function (result) {
2282
      if (result.orientation < 2 || result.orientation > 8) {
2283
        return deferred.resolve(file);
2284
      }
2285
      upload.dataUrl(file, true).then(function (url) {
2286
        var canvas = document.createElement('canvas');
2287
        var img = document.createElement('img');
2288
2289
        img.onload = function () {
2290
          try {
2291
            canvas.width = result.orientation > 4 ? img.height : img.width;
2292
            canvas.height = result.orientation > 4 ? img.width : img.height;
2293
            var ctx = canvas.getContext('2d');
2294
            applyTransform(ctx, result.orientation, img.width, img.height);
2295
            ctx.drawImage(img, 0, 0);
2296
            var dataUrl = canvas.toDataURL(file.type || 'image/WebP', 0.934);
2297
            dataUrl = upload.restoreExif(arrayBufferToBase64(result.fixedArrayBuffer), dataUrl);
2298
            var blob = upload.dataUrltoBlob(dataUrl, file.name);
2299
            deferred.resolve(blob);
2300
          } catch (e) {
2301
            return deferred.reject(e);
2302
          }
2303
        };
2304
        img.onerror = function () {
2305
          deferred.reject();
2306
        };
2307
        img.src = url;
2308
      }, function (e) {
2309
        deferred.reject(e);
2310
      });
2311
    }, function (e) {
2312
      deferred.reject(e);
2313
    });
2314
    return deferred.promise;
2315
  };
2316
2317
  upload.restoreExif = function (orig, resized) {
2318
    var ExifRestorer = {};
2319
2320
    ExifRestorer.KEY_STR = 'ABCDEFGHIJKLMNOP' +
2321
      'QRSTUVWXYZabcdef' +
2322
      'ghijklmnopqrstuv' +
2323
      'wxyz0123456789+/' +
2324
      '=';
2325
2326
    ExifRestorer.encode64 = function (input) {
2327
      var output = '',
2328
        chr1, chr2, chr3 = '',
2329
        enc1, enc2, enc3, enc4 = '',
2330
        i = 0;
2331
2332
      do {
2333
        chr1 = input[i++];
2334
        chr2 = input[i++];
2335
        chr3 = input[i++];
2336
2337
        enc1 = chr1 >> 2;
2338
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
2339
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
2340
        enc4 = chr3 & 63;
2341
2342
        if (isNaN(chr2)) {
2343
          enc3 = enc4 = 64;
2344
        } else if (isNaN(chr3)) {
2345
          enc4 = 64;
2346
        }
2347
2348
        output = output +
2349
          this.KEY_STR.charAt(enc1) +
2350
          this.KEY_STR.charAt(enc2) +
2351
          this.KEY_STR.charAt(enc3) +
2352
          this.KEY_STR.charAt(enc4);
2353
        chr1 = chr2 = chr3 = '';
2354
        enc1 = enc2 = enc3 = enc4 = '';
2355
      } while (i < input.length);
2356
2357
      return output;
2358
    };
2359
2360
    ExifRestorer.restore = function (origFileBase64, resizedFileBase64) {
2361
      if (origFileBase64.match('data:image/jpeg;base64,')) {
2362
        origFileBase64 = origFileBase64.replace('data:image/jpeg;base64,', '');
2363
      }
2364
2365
      var rawImage = this.decode64(origFileBase64);
2366
      var segments = this.slice2Segments(rawImage);
2367
2368
      var image = this.exifManipulation(resizedFileBase64, segments);
2369
2370
      return 'data:image/jpeg;base64,' + this.encode64(image);
2371
    };
2372
2373
2374
    ExifRestorer.exifManipulation = function (resizedFileBase64, segments) {
2375
      var exifArray = this.getExifArray(segments),
2376
        newImageArray = this.insertExif(resizedFileBase64, exifArray);
2377
      return new Uint8Array(newImageArray);
2378
    };
2379
2380
2381
    ExifRestorer.getExifArray = function (segments) {
2382
      var seg;
2383
      for (var x = 0; x < segments.length; x++) {
2384
        seg = segments[x];
2385
        if (seg[0] === 255 & seg[1] === 225) //(ff e1)
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
2386
        {
2387
          return seg;
2388
        }
2389
      }
2390
      return [];
2391
    };
2392
2393
2394
    ExifRestorer.insertExif = function (resizedFileBase64, exifArray) {
2395
      var imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''),
2396
        buf = this.decode64(imageData),
2397
        separatePoint = buf.indexOf(255, 3),
2398
        mae = buf.slice(0, separatePoint),
2399
        ato = buf.slice(separatePoint),
2400
        array = mae;
2401
2402
      array = array.concat(exifArray);
2403
      array = array.concat(ato);
2404
      return array;
2405
    };
2406
2407
2408
    ExifRestorer.slice2Segments = function (rawImageArray) {
2409
      var head = 0,
2410
        segments = [];
2411
2412
      while (1) {
2413
        if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
2414
          break;
2415
        }
2416
        if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
2417
          head += 2;
2418
        }
2419
        else {
2420
          var length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3],
2421
            endPoint = head + length + 2,
2422
            seg = rawImageArray.slice(head, endPoint);
2423
          segments.push(seg);
2424
          head = endPoint;
2425
        }
2426
        if (head > rawImageArray.length) {
2427
          break;
2428
        }
2429
      }
2430
2431
      return segments;
2432
    };
2433
2434
2435
    ExifRestorer.decode64 = function (input) {
2436
      var chr1, chr2, chr3 = '',
2437
        enc1, enc2, enc3, enc4 = '',
2438
        i = 0,
2439
        buf = [];
2440
2441
      // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
2442
      var base64test = /[^A-Za-z0-9\+\/\=]/g;
2443
      if (base64test.exec(input)) {
2444
        console.log('There were invalid base64 characters in the input text.\n' +
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
2445
          'Valid base64 characters are A-Z, a-z, 0-9, ' + ', ' / ',and "="\n' +
2446
          'Expect errors in decoding.');
2447
      }
2448
      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
2449
2450
      do {
2451
        enc1 = this.KEY_STR.indexOf(input.charAt(i++));
2452
        enc2 = this.KEY_STR.indexOf(input.charAt(i++));
2453
        enc3 = this.KEY_STR.indexOf(input.charAt(i++));
2454
        enc4 = this.KEY_STR.indexOf(input.charAt(i++));
2455
2456
        chr1 = (enc1 << 2) | (enc2 >> 4);
2457
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2458
        chr3 = ((enc3 & 3) << 6) | enc4;
2459
2460
        buf.push(chr1);
2461
2462
        if (enc3 !== 64) {
2463
          buf.push(chr2);
2464
        }
2465
        if (enc4 !== 64) {
2466
          buf.push(chr3);
2467
        }
2468
2469
        chr1 = chr2 = chr3 = '';
2470
        enc1 = enc2 = enc3 = enc4 = '';
2471
2472
      } while (i < input.length);
2473
2474
      return buf;
2475
    };
2476
2477
    return ExifRestorer.restore(orig, resized);  //<= EXIF
2478
  };
2479
2480
  return upload;
2481
}]);
2482
2483