cmsify 0.0.1

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