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/src/FileAPI.js   F
last analyzed

Complexity

Total Complexity 746
Complexity/F 2.21

Size

Lines of Code 4292
Function Count 337

Duplication

Duplicated Lines 4292
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 2346
dl 4292
loc 4292
rs 0.8
c 0
b 0
f 0
wmc 746
mnd 409
bc 409
fnc 337
bpm 1.2136
cpm 2.2136
noi 34

32 Functions

Rating   Name   Duplication   Size   Complexity  
F FileAPI.js ➔ _transform 63 63 67
A FileAPI.js ➔ _inherit 9 9 3
F FileAPI.js ➔ _addFile 58 58 15
A FileAPI.js ➔ _getDimensions 29 29 4
B FileAPI.js ➔ _readAs 37 37 7
A FileAPI.js ➔ _unwrap 7 7 2
F FileAPI.js ➔ _throttle 11 11 22
A FileAPI.js ➔ _isHtmlFile 3 3 1
A FileAPI.js ➔ _detectVideoSignal 10 10 2
F FileAPI.js ➔ _px 3 3 94
D FileAPI.js ➔ _next 3 3 13
A FileAPI.js ➔ _getFileDescr 8 8 1
A FileAPI.js ➔ _hasSupportReadAs 3 3 1
F FileAPI.js ➔ upload 26 26 25
B FileAPI.js ➔ _isRegularFile 28 28 6
A FileAPI.js ➔ isInputFile 3 3 1
B FileAPI.js ➔ _makeFlashImage 44 44 6
D FileAPI.js ➔ _readEntryAsFiles 47 47 12
A FileAPI.js ➔ _getAsEntry 6 6 3
A FileAPI.js ➔ _simpleClone 10 10 3
A FileAPI.js ➔ _wrap 5 5 5
A FileAPI.js ➔ _css 13 13 5
B FileAPI.js ➔ add 16 16 6
A FileAPI.js ➔ _ 4 4 5
A FileAPI.js ➔ _makeFlashHTML 13 13 3
F FileAPI.js ➔ _fn 9 9 32
B FileAPI.js ➔ _getUrl 23 23 7
F FileAPI.js ➔ Image 28 28 34
A FileAPI.js ➔ _isOriginTransform 11 11 4
F FileAPI.js ➔ _convertFile 42 42 74
F FileAPI.js ➔ _emit 9 9 20
A FileAPI.js ➔ _getDataTransfer 3 3 1

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/src/FileAPI.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
/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git
2
 * FileAPI — a set of  javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
3
 */
4
5
/*
6
 * JavaScript Canvas to Blob 2.0.5
7
 * https://github.com/blueimp/JavaScript-Canvas-to-Blob
8
 *
9
 * Copyright 2012, Sebastian Tschan
10
 * https://blueimp.net
11
 *
12
 * Licensed under the MIT license:
13
 * http://www.opensource.org/licenses/MIT
14
 *
15
 * Based on stackoverflow user Stoive's code snippet:
16
 * http://stackoverflow.com/q/4998908
17
 */
18
19
/*jslint nomen: true, regexp: true */
20
/*global window, atob, Blob, ArrayBuffer, Uint8Array */
21
22 View Code Duplication
(function (window) {
23
    'use strict';
24
    var CanvasPrototype = window.HTMLCanvasElement &&
25
            window.HTMLCanvasElement.prototype,
26
        hasBlobConstructor = window.Blob && (function () {
27
            try {
28
                return Boolean(new Blob());
29
            } catch (e) {
30
                return false;
31
            }
32
        }()),
33
        hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
34
            (function () {
35
                try {
36
                    return new Blob([new Uint8Array(100)]).size === 100;
37
                } catch (e) {
38
                    return false;
39
                }
40
            }()),
41
        BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
42
            window.MozBlobBuilder || window.MSBlobBuilder,
43
        dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
44
            window.ArrayBuffer && window.Uint8Array && function (dataURI) {
45
                var byteString,
46
                    arrayBuffer,
47
                    intArray,
48
                    i,
49
                    mimeString,
50
                    bb;
51
                if (dataURI.split(',')[0].indexOf('base64') >= 0) {
52
                    // Convert base64 to raw binary data held in a string:
53
                    byteString = atob(dataURI.split(',')[1]);
54
                } else {
55
                    // Convert base64/URLEncoded data component to raw binary data:
56
                    byteString = decodeURIComponent(dataURI.split(',')[1]);
57
                }
58
                // Write the bytes of the string to an ArrayBuffer:
59
                arrayBuffer = new ArrayBuffer(byteString.length);
60
                intArray = new Uint8Array(arrayBuffer);
61
                for (i = 0; i < byteString.length; i += 1) {
62
                    intArray[i] = byteString.charCodeAt(i);
63
                }
64
                // Separate out the mime component:
65
                mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
66
                // Write the ArrayBuffer (or ArrayBufferView) to a blob:
67
                if (hasBlobConstructor) {
68
                    return new Blob(
69
                        [hasArrayBufferViewSupport ? intArray : arrayBuffer],
70
                        {type: mimeString}
71
                    );
72
                }
73
                bb = new BlobBuilder();
74
                bb.append(arrayBuffer);
75
                return bb.getBlob(mimeString);
76
            };
77
    if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
78
        if (CanvasPrototype.mozGetAsFile) {
79
            CanvasPrototype.toBlob = function (callback, type, quality) {
80
                if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
81
                    callback(dataURLtoBlob(this.toDataURL(type, quality)));
82
                } else {
83
                    callback(this.mozGetAsFile('blob', type));
84
                }
85
            };
86
        } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
87
            CanvasPrototype.toBlob = function (callback, type, quality) {
88
                callback(dataURLtoBlob(this.toDataURL(type, quality)));
89
            };
90
        }
91
    }
92
    window.dataURLtoBlob = dataURLtoBlob;
93
})(window);
94
95
/*jslint evil: true */
96
/*global window, URL, webkitURL, ActiveXObject */
97
98
(function (window, undef){
99
	'use strict';
100
101
	var
102
		gid = 1,
103
		noop = function (){},
104
105
		document = window.document,
106
		doctype = document.doctype || {},
107
		userAgent = window.navigator.userAgent,
108
109
		// https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48
110
		apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL),
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable URL is declared in the current environment, consider using typeof URL === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
Best Practice introduced by
If you intend to check if the variable webkitURL is declared in the current environment, consider using typeof webkitURL === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
111
112
		Blob = window.Blob,
113
		File = window.File,
114
		FileReader = window.FileReader,
115
		FormData = window.FormData,
116
117
118
		XMLHttpRequest = window.XMLHttpRequest,
119
		jQuery = window.jQuery,
120
121
		html5 =    !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary)))
122
				&& !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
123
124
		cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
125
126
		chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
127
128
		// https://github.com/blueimp/JavaScript-Canvas-to-Blob
129
		dataURLtoBlob = window.dataURLtoBlob,
130
131
132
		_rimg = /img/i,
133
		_rcanvas = /canvas/i,
134
		_rimgcanvas = /img|canvas/i,
135
		_rinput = /input/i,
136
		_rdata = /^data:[^,]+,/,
137
138
		_toString = {}.toString,
139
140
141
		Math = window.Math,
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Math. This makes code hard to read, consider using a different name.
Loading history...
142
143
		_SIZE_CONST = function (pow){
144
			pow = new window.Number(Math.pow(1024, pow));
145
			pow.from = function (sz){ return Math.round(sz * this); };
146
			return	pow;
147
		},
148
149
		_elEvents = {}, // element event listeners
150
		_infoReader = [], // list of file info processors
151
152
		_readerEvents = 'abort progress error load loadend',
153
		_xhrPropsExport = 'status statusText readyState response responseXML responseText responseBody'.split(' '),
154
155
		currentTarget = 'currentTarget', // for minimize
156
		preventDefault = 'preventDefault', // and this too
157
158
		_isArray = function (ar) {
159
			return	ar && ('length' in ar);
160
		},
161
162
		/**
163
		 * Iterate over a object or array
164
		 */
165
		_each = function (obj, fn, ctx){
166
			if( obj ){
167
				if( _isArray(obj) ){
168
					for( var i = 0, n = obj.length; i < n; i++ ){
169
						if( i in obj ){
170
							fn.call(ctx, obj[i], i, obj);
171
						}
172
					}
173
				}
174
				else {
175
					for( var key in obj ){
176
						if( obj.hasOwnProperty(key) ){
177
							fn.call(ctx, obj[key], key, obj);
178
						}
179
					}
180
				}
181
			}
182
		},
183
184
		/**
185
		 * Merge the contents of two or more objects together into the first object
186
		 */
187
		_extend = function (dst){
188
			var args = arguments, i = 1, _ext = function (val, key){ dst[key] = val; };
189
			for( ; i < args.length; i++ ){
190
				_each(args[i], _ext);
191
			}
192
			return  dst;
193
		},
194
195
		/**
196
		 * Add event listener
197
		 */
198
		_on = function (el, type, fn){
199
			if( el ){
200
				var uid = api.uid(el);
201
202
				if( !_elEvents[uid] ){
203
					_elEvents[uid] = {};
204
				}
205
206
				var isFileReader = (FileReader && el) && (el instanceof FileReader);
207
				_each(type.split(/\s+/), function (type){
208
					if( jQuery && !isFileReader){
209
						jQuery.event.add(el, type, fn);
210
					} else {
211
						if( !_elEvents[uid][type] ){
212
							_elEvents[uid][type] = [];
213
						}
214
215
						_elEvents[uid][type].push(fn);
216
217
						if( el.addEventListener ){ el.addEventListener(type, fn, false); }
218
						else if( el.attachEvent ){ el.attachEvent('on'+type, fn); }
219
						else { el['on'+type] = fn; }
220
					}
221
				});
222
			}
223
		},
224
225
226
		/**
227
		 * Remove event listener
228
		 */
229
		_off = function (el, type, fn){
230
			if( el ){
231
				var uid = api.uid(el), events = _elEvents[uid] || {};
232
233
				var isFileReader = (FileReader && el) && (el instanceof FileReader);
234
				_each(type.split(/\s+/), function (type){
235
					if( jQuery && !isFileReader){
236
						jQuery.event.remove(el, type, fn);
237
					}
238
					else {
239
						var fns = events[type] || [], i = fns.length;
240
241
						while( i-- ){
242
							if( fns[i] === fn ){
243
								fns.splice(i, 1);
244
								break;
245
							}
246
						}
247
248
						if( el.addEventListener ){ el.removeEventListener(type, fn, false); }
249
						else if( el.detachEvent ){ el.detachEvent('on'+type, fn); }
250
						else { el['on'+type] = null; }
251
					}
252
				});
253
			}
254
		},
255
256
257
		_one = function(el, type, fn){
258
			_on(el, type, function _(evt){
259
				_off(el, type, _);
260
				fn(evt);
261
			});
262
		},
263
264
265
		_fixEvent = function (evt){
266
			if( !evt.target ){ evt.target = window.event && window.event.srcElement || document; }
267
			if( evt.target.nodeType === 3 ){ evt.target = evt.target.parentNode; }
268
			return  evt;
269
		},
270
271
272
		_supportInputAttr = function (attr){
273
			var input = document.createElement('input');
274
			input.setAttribute('type', "file");
275
			return attr in input;
276
		},
277
278
		/**
279
		 * FileAPI (core object)
280
		 */
281
		api = {
282
			version: '2.0.7',
283
284
			cors: false,
285
			html5: true,
286
			media: false,
287
			formData: true,
288
			multiPassResize: true,
289
290
			debug: false,
291
			pingUrl: false,
292
			multiFlash: false,
293
			flashAbortTimeout: 0,
294
			withCredentials: true,
295
296
			staticPath: './dist/',
297
298
			flashUrl: 0, // @default: './FileAPI.flash.swf'
299
			flashImageUrl: 0, // @default: './FileAPI.flash.image.swf'
300
301
			postNameConcat: function (name, idx){
302
				return	name + (idx != null ? '['+ idx +']' : '');
303
			},
304
305
			ext2mime: {
306
				  jpg:	'image/jpeg'
307
				, tif:	'image/tiff'
308
				, txt:	'text/plain'
309
			},
310
311
			// Fallback for flash
312
			accept: {
313
				  'image/*': 'art bm bmp dwg dxf cbr cbz fif fpx gif ico iefs jfif jpe jpeg jpg jps jut mcf nap nif pbm pcx pgm pict pm png pnm qif qtif ras rast rf rp svf tga tif tiff xbm xbm xpm xwd'
314
				, 'audio/*': 'm4a flac aac rm mpa wav wma ogg mp3 mp2 m3u mod amf dmf dsm far gdm imf it m15 med okt s3m stm sfx ult uni xm sid ac3 dts cue aif aiff wpl ape mac mpc mpp shn wv nsf spc gym adplug adx dsp adp ymf ast afc hps xs'
315
				, 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
316
			},
317
318
			uploadRetry : 0,
319
			networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
320
321
			chunkSize : 0,
322
			chunkUploadRetry : 0,
323
			chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
324
325
			KB: _SIZE_CONST(1),
326
			MB: _SIZE_CONST(2),
327
			GB: _SIZE_CONST(3),
328
			TB: _SIZE_CONST(4),
329
330
			EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
331
332
			expando: 'fileapi' + (new Date).getTime(),
333
334
			uid: function (obj){
335
				return	obj
336
					? (obj[api.expando] = obj[api.expando] || api.uid())
337
					: (++gid, api.expando + gid)
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
338
				;
339
			},
340
341
			log: function (){
342
				// ngf fix for IE8 #1071
343
				if( api.debug && api._supportConsoleLog ){
344
					if( api._supportConsoleLogApply ){
345
						console.log.apply(console, arguments);
346
					}
347
					else {
348
						console.log([].join.call(arguments, ' '));
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...
349
					}
350
				}
351
			},
352
353
			/**
354
			 * Create new image
355
			 *
356
			 * @param {String} [src]
357
			 * @param {Function} [fn]   1. error -- boolean, 2. img -- Image element
358
			 * @returns {HTMLElement}
359
			 */
360
			newImage: function (src, fn){
361
				var img = document.createElement('img');
362
				if( fn ){
363
					api.event.one(img, 'error load', function (evt){
364
						fn(evt.type == 'error', img);
365
						img = null;
366
					});
367
				}
368
				img.src = src;
369
				return	img;
370
			},
371
372
			/**
373
			 * Get XHR
374
			 * @returns {XMLHttpRequest}
375
			 */
376
			getXHR: function (){
377
				var xhr;
378
379
				if( XMLHttpRequest ){
380
					xhr = new XMLHttpRequest;
381
				}
382
				else if( window.ActiveXObject ){
383
					try {
384
						xhr = new ActiveXObject('MSXML2.XMLHttp.3.0');
385
					} catch (e) {
386
						xhr = new ActiveXObject('Microsoft.XMLHTTP');
387
					}
388
				}
389
390
				return  xhr;
391
			},
392
393
			isArray: _isArray,
394
395
			support: {
396
				dnd:     cors && ('ondrop' in document.createElement('div')),
397
				cors:    cors,
398
				html5:   html5,
399
				chunked: chunked,
400
				dataURI: true,
401
				accept:   _supportInputAttr('accept'),
402
				multiple: _supportInputAttr('multiple')
403
			},
404
405
			event: {
406
				  on: _on
407
				, off: _off
408
				, one: _one
409
				, fix: _fixEvent
410
			},
411
412
413
			throttle: function(fn, delay) {
414
				var id, args;
415
416
				return function _throttle(){
417
					args = arguments;
418
419
					if( !id ){
420
						fn.apply(window, args);
421
						id = setTimeout(function (){
422
							id = 0;
423
							fn.apply(window, args);
424
						}, delay);
425
					}
426
				};
427
			},
428
429
430
			F: function (){},
431
432
433
			parseJSON: function (str){
434
				var json;
435
				if( window.JSON && JSON.parse ){
436
					json = JSON.parse(str);
437
				}
438
				else {
439
					json = (new Function('return ('+str.replace(/([\r\n])/g, '\\$1')+');'))();
0 ignored issues
show
Performance Best Practice introduced by
Using new Function() to create a function is slow and difficult to debug. Such functions do not create a closure. Consider using another way to define your function.
Loading history...
440
				}
441
				return json;
442
			},
443
444
445
			trim: function (str){
446
				str = String(str);
447
				return	str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
448
			},
449
450
			/**
451
			 * Simple Defer
452
			 * @return	{Object}
453
			 */
454
			defer: function (){
455
				var
456
					  list = []
457
					, result
458
					, error
459
					, defer = {
460
						resolve: function (err, res){
461
							defer.resolve = noop;
462
							error	= err || false;
463
							result	= res;
464
465
							while( res = list.shift() ){
466
								res(error, result);
467
							}
468
						},
469
470
						then: function (fn){
471
							if( error !== undef ){
472
								fn(error, result);
473
							} else {
474
								list.push(fn);
475
							}
476
						}
477
				};
478
479
				return	defer;
480
			},
481
482
			queue: function (fn){
483
				var
484
					  _idx = 0
485
					, _length = 0
486
					, _fail = false
487
					, _end = false
488
					, queue = {
489
						inc: function (){
490
							_length++;
491
						},
492
493
						next: function (){
494
							_idx++;
495
							setTimeout(queue.check, 0);
496
						},
497
498
						check: function (){
499
							(_idx >= _length) && !_fail && queue.end();
500
						},
501
502
						isFail: function (){
503
							return _fail;
504
						},
505
506
						fail: function (){
507
							!_fail && fn(_fail = true);
508
						},
509
510
						end: function (){
511
							if( !_end ){
512
								_end = true;
513
								fn();
514
							}
515
						}
516
					}
517
				;
518
				return queue;
519
			},
520
521
522
			/**
523
			 * For each object
524
			 *
525
			 * @param	{Object|Array}	obj
526
			 * @param	{Function}		fn
527
			 * @param	{*}				[ctx]
528
			 */
529
			each: _each,
530
531
532
			/**
533
			 * Async for
534
			 * @param {Array} array
535
			 * @param {Function} callback
536
			 */
537
			afor: function (array, callback){
538
				var i = 0, n = array.length;
539
540
				if( _isArray(array) && n-- ){
541
					(function _next(){
542
						callback(n != i && _next, array[i], i++);
543
					})();
544
				}
545
				else {
546
					callback(false);
547
				}
548
			},
549
550
551
			/**
552
			 * Merge the contents of two or more objects together into the first object
553
			 *
554
			 * @param	{Object}	dst
555
			 * @return	{Object}
556
			 */
557
			extend: _extend,
558
559
560
			/**
561
			 * Is file?
562
			 * @param  {File}  file
563
			 * @return {Boolean}
564
			 */
565
			isFile: function (file){
566
				return _toString.call(file) === '[object File]';
567
			},
568
569
570
			/**
571
			 * Is blob?
572
			 * @param   {Blob}  blob
573
			 * @returns {Boolean}
574
			 */
575
			isBlob: function (blob) {
576
				return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
577
			},
578
579
580
			/**
581
			 * Is canvas element
582
			 *
583
			 * @param	{HTMLElement}	el
584
			 * @return	{Boolean}
585
			 */
586
			isCanvas: function (el){
587
				return	el && _rcanvas.test(el.nodeName);
588
			},
589
590
591
			getFilesFilter: function (filter){
592
				filter = typeof filter == 'string' ? filter : (filter.getAttribute && filter.getAttribute('accept') || '');
593
				return	filter ? new RegExp('('+ filter.replace(/\./g, '\\.').replace(/,/g, '|') +')$', 'i') : /./;
594
			},
595
596
597
598
			/**
599
			 * Read as DataURL
600
			 *
601
			 * @param {File|Element} file
602
			 * @param {Function} fn
603
			 */
604
			readAsDataURL: function (file, fn){
605
				if( api.isCanvas(file) ){
606
					_emit(file, fn, 'load', api.toDataURL(file));
607
				}
608
				else {
609
					_readAs(file, fn, 'DataURL');
610
				}
611
			},
612
613
614
			/**
615
			 * Read as Binary string
616
			 *
617
			 * @param {File} file
618
			 * @param {Function} fn
619
			 */
620
			readAsBinaryString: function (file, fn){
621
				if( _hasSupportReadAs('BinaryString') ){
622
					_readAs(file, fn, 'BinaryString');
623
				} else {
624
					// Hello IE10!
625
					_readAs(file, function (evt){
626
						if( evt.type == 'load' ){
627
							try {
628
								// dataURL -> binaryString
629
								evt.result = api.toBinaryString(evt.result);
630
							} catch (e){
631
								evt.type = 'error';
632
								evt.message = e.toString();
633
							}
634
						}
635
						fn(evt);
636
					}, 'DataURL');
637
				}
638
			},
639
640
641
			/**
642
			 * Read as ArrayBuffer
643
			 *
644
			 * @param {File} file
645
			 * @param {Function} fn
646
			 */
647
			readAsArrayBuffer: function(file, fn){
648
				_readAs(file, fn, 'ArrayBuffer');
649
			},
650
651
652
			/**
653
			 * Read as text
654
			 *
655
			 * @param {File} file
656
			 * @param {String} encoding
657
			 * @param {Function} [fn]
658
			 */
659
			readAsText: function(file, encoding, fn){
660
				if( !fn ){
661
					fn	= encoding;
662
					encoding = 'utf-8';
663
				}
664
665
				_readAs(file, fn, 'Text', encoding);
666
			},
667
668
669
			/**
670
			 * Convert image or canvas to DataURL
671
			 *
672
			 * @param   {Element}  el      Image or Canvas element
673
			 * @param   {String}   [type]  mime-type
674
			 * @return  {String}
675
			 */
676
			toDataURL: function (el, type){
677
				if( typeof el == 'string' ){
678
					return  el;
679
				}
680
				else if( el.toDataURL ){
681
					return  el.toDataURL(type || 'image/png');
682
				}
683
			},
684
685
686
			/**
687
			 * Canvert string, image or canvas to binary string
688
			 *
689
			 * @param   {String|Element} val
690
			 * @return  {String}
691
			 */
692
			toBinaryString: function (val){
693
				return  window.atob(api.toDataURL(val).replace(_rdata, ''));
694
			},
695
696
697
			/**
698
			 * Read file or DataURL as ImageElement
699
			 *
700
			 * @param	{File|String}	file
701
			 * @param	{Function}		fn
702
			 * @param	{Boolean}		[progress]
703
			 */
704
			readAsImage: function (file, fn, progress){
705
				if( api.isFile(file) ){
706
					if( apiURL ){
707
						/** @namespace apiURL.createObjectURL */
708
						var data = apiURL.createObjectURL(file);
709
						if( data === undef ){
710
							_emit(file, fn, 'error');
711
						}
712
						else {
713
							api.readAsImage(data, fn, progress);
714
						}
715
					}
716
					else {
717
						api.readAsDataURL(file, function (evt){
718
							if( evt.type == 'load' ){
719
								api.readAsImage(evt.result, fn, progress);
720
							}
721
							else if( progress || evt.type == 'error' ){
722
								_emit(file, fn, evt, null, { loaded: evt.loaded, total: evt.total });
723
							}
724
						});
725
					}
726
				}
727
				else if( api.isCanvas(file) ){
728
					_emit(file, fn, 'load', file);
729
				}
730
				else if( _rimg.test(file.nodeName) ){
731
					if( file.complete ){
732
						_emit(file, fn, 'load', file);
733
					}
734
					else {
735
						var events = 'error abort load';
736
						_one(file, events, function _fn(evt){
737
							if( evt.type == 'load' && apiURL ){
738
								/** @namespace apiURL.revokeObjectURL */
739
								apiURL.revokeObjectURL(file.src);
740
							}
741
742
							_off(file, events, _fn);
743
							_emit(file, fn, evt, file);
744
						});
745
					}
746
				}
747
				else if( file.iframe ){
748
					_emit(file, fn, { type: 'error' });
749
				}
750
				else {
751
					// Created image
752
					var img = api.newImage(file.dataURL || file);
753
					api.readAsImage(img, fn, progress);
754
				}
755
			},
756
757
758
			/**
759
			 * Make file by name
760
			 *
761
			 * @param	{String}	name
762
			 * @return	{Array}
763
			 */
764
			checkFileObj: function (name){
765
				var file = {}, accept = api.accept;
766
767
				if( typeof name == 'object' ){
768
					file = name;
769
				}
770
				else {
771
					file.name = (name + '').split(/\\|\//g).pop();
772
				}
773
774
				if( file.type == null ){
775
					file.type = file.name.split('.').pop();
776
				}
777
778
				_each(accept, function (ext, type){
779
					ext = new RegExp(ext.replace(/\s/g, '|'), 'i');
780
					if( ext.test(file.type) || api.ext2mime[file.type] ){
781
						file.type = api.ext2mime[file.type] || (type.split('/')[0] +'/'+ file.type);
782
					}
783
				});
784
785
				return	file;
786
			},
787
788
789
			/**
790
			 * Get drop files
791
			 *
792
			 * @param	{Event}	evt
793
			 * @param	{Function} callback
794
			 */
795
			getDropFiles: function (evt, callback){
796
				var
797
					  files = []
798
					, dataTransfer = _getDataTransfer(evt)
799
					, entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
800
					, queue = api.queue(function (){ callback(files); })
801
				;
802
803
				_each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
804
					queue.inc();
805
806
					try {
807
						if( entrySupport ){
808
							_readEntryAsFiles(item, function (err, entryFiles){
809
								if( err ){
810
									api.log('[err] getDropFiles:', err);
811
								} else {
812
									files.push.apply(files, entryFiles);
813
								}
814
								queue.next();
815
							});
816
						}
817
						else {
818
							_isRegularFile(item, function (yes){
819
								yes && files.push(item);
820
								queue.next();
821
							});
822
						}
823
					}
824
					catch( err ){
825
						queue.next();
826
						api.log('[err] getDropFiles: ', err);
827
					}
828
				});
829
830
				queue.check();
831
			},
832
833
834
			/**
835
			 * Get file list
836
			 *
837
			 * @param	{HTMLInputElement|Event}	input
838
			 * @param	{String|Function}	[filter]
839
			 * @param	{Function}			[callback]
840
			 * @return	{Array|Null}
841
			 */
842
			getFiles: function (input, filter, callback){
843
				var files = [];
844
845
				if( callback ){
846
					api.filterFiles(api.getFiles(input), filter, callback);
847
					return null;
848
				}
849
850
				if( input.jquery ){
851
					// jQuery object
852
					input.each(function (){
853
						files = files.concat(api.getFiles(this));
854
					});
855
					input	= files;
856
					files	= [];
857
				}
858
859
				if( typeof filter == 'string' ){
860
					filter	= api.getFilesFilter(filter);
861
				}
862
863
				if( input.originalEvent ){
864
					// jQuery event
865
					input = _fixEvent(input.originalEvent);
866
				}
867
				else if( input.srcElement ){
868
					// IE Event
869
					input = _fixEvent(input);
870
				}
871
872
873
				if( input.dataTransfer ){
874
					// Drag'n'Drop
875
					input = input.dataTransfer;
876
				}
877
				else if( input.target ){
878
					// Event
879
					input = input.target;
880
				}
881
882
				if( input.files ){
883
					// Input[type="file"]
884
					files = input.files;
885
886
					if( !html5 ){
887
						// Partial support for file api
888
						files[0].blob	= input;
889
						files[0].iframe	= true;
890
					}
891
				}
892
				else if( !html5 && isInputFile(input) ){
893
					if( api.trim(input.value) ){
894
						files = [api.checkFileObj(input.value)];
895
						files[0].blob   = input;
896
						files[0].iframe = true;
897
					}
898
				}
899
				else if( _isArray(input) ){
900
					files	= input;
901
				}
902
903
				return	api.filter(files, function (file){ return !filter || filter.test(file.name); });
904
			},
905
906
907
			/**
908
			 * Get total file size
909
			 * @param	{Array}	files
910
			 * @return	{Number}
911
			 */
912
			getTotalSize: function (files){
913
				var size = 0, i = files && files.length;
914
				while( i-- ){
915
					size += files[i].size;
916
				}
917
				return	size;
918
			},
919
920
921
			/**
922
			 * Get image information
923
			 *
924
			 * @param	{File}		file
925
			 * @param	{Function}	fn
926
			 */
927
			getInfo: function (file, fn){
928
				var info = {}, readers = _infoReader.concat();
929
930
				if( api.isFile(file) ){
931
					(function _next(){
932
						var reader = readers.shift();
933
						if( reader ){
934
							if( reader.test(file.type) ){
935
								reader(file, function (err, res){
936
									if( err ){
937
										fn(err);
938
									}
939
									else {
940
										_extend(info, res);
941
										_next();
942
									}
943
								});
944
							}
945
							else {
946
								_next();
947
							}
948
						}
949
						else {
950
							fn(false, info);
951
						}
952
					})();
953
				}
954
				else {
955
					fn('not_support_info', info);
956
				}
957
			},
958
959
960
			/**
961
			 * Add information reader
962
			 *
963
			 * @param {RegExp} mime
964
			 * @param {Function} fn
965
			 */
966
			addInfoReader: function (mime, fn){
967
				fn.test = function (type){ return mime.test(type); };
968
				_infoReader.push(fn);
969
			},
970
971
972
			/**
973
			 * Filter of array
974
			 *
975
			 * @param	{Array}		input
976
			 * @param	{Function}	fn
977
			 * @return	{Array}
978
			 */
979
			filter: function (input, fn){
980
				var result = [], i = 0, n = input.length, val;
981
982
				for( ; i < n; i++ ){
983
					if( i in input ){
984
						val = input[i];
985
						if( fn.call(val, val, i, input) ){
986
							result.push(val);
987
						}
988
					}
989
				}
990
991
				return	result;
992
			},
993
994
995
			/**
996
			 * Filter files
997
			 *
998
			 * @param	{Array}		files
999
			 * @param	{Function}	eachFn
1000
			 * @param	{Function}	resultFn
1001
			 */
1002
			filterFiles: function (files, eachFn, resultFn){
1003
				if( files.length ){
1004
					// HTML5 or Flash
1005
					var queue = files.concat(), file, result = [], deleted = [];
1006
1007
					(function _next(){
1008
						if( queue.length ){
1009
							file = queue.shift();
1010
							api.getInfo(file, function (err, info){
1011
								(eachFn(file, err ? false : info) ? result : deleted).push(file);
1012
								_next();
1013
							});
1014
						}
1015
						else {
1016
							resultFn(result, deleted);
1017
						}
1018
					})();
1019
				}
1020
				else {
1021
					resultFn([], files);
1022
				}
1023
			},
1024
1025
1026
			upload: function (options){
1027
				options = _extend({
1028
					  jsonp: 'callback'
1029
					, prepare: api.F
1030
					, beforeupload: api.F
1031
					, upload: api.F
1032
					, fileupload: api.F
1033
					, fileprogress: api.F
1034
					, filecomplete: api.F
1035
					, progress: api.F
1036
					, complete: api.F
1037
					, pause: api.F
1038
					, imageOriginal: true
1039
					, chunkSize: api.chunkSize
1040
					, chunkUploadRetry: api.chunkUploadRetry
1041
					, uploadRetry: api.uploadRetry
1042
				}, options);
1043
1044
1045
				if( options.imageAutoOrientation && !options.imageTransform ){
1046
					options.imageTransform = { rotate: 'auto' };
1047
				}
1048
1049
1050
				var
1051
					  proxyXHR = new api.XHR(options)
1052
					, dataArray = this._getFilesDataArray(options.files)
1053
					, _this = this
1054
					, _total = 0
1055
					, _loaded = 0
1056
					, _nextFile
1057
					, _complete = false
1058
				;
1059
1060
1061
				// calc total size
1062
				_each(dataArray, function (data){
1063
					_total += data.size;
1064
				});
1065
1066
				// Array of files
1067
				proxyXHR.files = [];
1068
				_each(dataArray, function (data){
1069
					proxyXHR.files.push(data.file);
1070
				});
1071
1072
				// Set upload status props
1073
				proxyXHR.total	= _total;
1074
				proxyXHR.loaded	= 0;
1075
				proxyXHR.filesLeft = dataArray.length;
1076
1077
				// emit "beforeupload"  event
1078
				options.beforeupload(proxyXHR, options);
1079
1080
				// Upload by file
1081
				_nextFile = function (){
1082
					var
1083
						  data = dataArray.shift()
1084
						, _file = data && data.file
1085
						, _fileLoaded = false
1086
						, _fileOptions = _simpleClone(options)
1087
					;
1088
1089
					proxyXHR.filesLeft = dataArray.length;
1090
1091
					if( _file && _file.name === api.expando ){
1092
						_file = null;
1093
						api.log('[warn] FileAPI.upload() — called without files');
1094
					}
1095
1096
					if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
1097
						// Mark active job
1098
						_complete = false;
1099
1100
						// Set current upload file
1101
						proxyXHR.currentFile = _file;
1102
1103
						// Prepare file options
1104
						if (_file && options.prepare(_file, _fileOptions) === false) {
1105
							_nextFile.call(_this);
1106
							return;
1107
						}
1108
						_fileOptions.file = _file;
1109
1110
						_this._getFormData(_fileOptions, data, function (form){
1111
							if( !_loaded ){
1112
								// emit "upload" event
1113
								options.upload(proxyXHR, options);
1114
							}
1115
1116
							var xhr = new api.XHR(_extend({}, _fileOptions, {
1117
1118
								upload: _file ? function (){
1119
									// emit "fileupload" event
1120
									options.fileupload(_file, xhr, _fileOptions);
1121
								} : noop,
1122
1123
								progress: _file ? function (evt){
1124
									if( !_fileLoaded ){
1125
										// For ignore the double calls.
1126
										_fileLoaded = (evt.loaded === evt.total);
1127
1128
										// emit "fileprogress" event
1129
										options.fileprogress({
1130
											  type:   'progress'
1131
											, total:  data.total = evt.total
1132
											, loaded: data.loaded = evt.loaded
1133
										}, _file, xhr, _fileOptions);
1134
1135
										// emit "progress" event
1136
										options.progress({
1137
											  type:   'progress'
1138
											, total:  _total
1139
											, loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
1140
										}, _file, xhr, _fileOptions);
1141
									}
1142
								} : noop,
1143
1144
								complete: function (err){
1145
									_each(_xhrPropsExport, function (name){
1146
										proxyXHR[name] = xhr[name];
1147
									});
1148
1149
									if( _file ){
1150
										data.total = (data.total || data.size);
1151
										data.loaded	= data.total;
1152
1153
										if( !err ) {
1154
											// emulate 100% "progress"
1155
											this.progress(data);
1156
1157
											// fixed throttle event
1158
											_fileLoaded = true;
1159
1160
											// bytes loaded
1161
											_loaded += data.size; // data.size != data.total, it's desirable fix this
1162
											proxyXHR.loaded = _loaded;
1163
										}
1164
1165
										// emit "filecomplete" event
1166
										options.filecomplete(err, xhr, _file, _fileOptions);
1167
									}
1168
1169
									// upload next file
1170
									setTimeout(function () {_nextFile.call(_this);}, 0);
1171
								}
1172
							})); // xhr
1173
1174
1175
							// ...
1176
							proxyXHR.abort = function (current){
1177
								if (!current) { dataArray.length = 0; }
1178
								this.current = current;
1179
								xhr.abort();
1180
							};
1181
1182
							// Start upload
1183
							xhr.send(form);
1184
						});
1185
					}
1186
					else {
1187
						var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
1188
						options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
1189
						// Mark done state
1190
						_complete = true;
1191
					}
1192
				};
1193
1194
1195
				// Next tick
1196
				setTimeout(_nextFile, 0);
1197
1198
1199
				// Append more files to the existing request
1200
				// first - add them to the queue head/tail
1201
				proxyXHR.append = function (files, first) {
1202
					files = api._getFilesDataArray([].concat(files));
1203
1204
					_each(files, function (data) {
1205
						_total += data.size;
1206
						proxyXHR.files.push(data.file);
1207
						if (first) {
1208
							dataArray.unshift(data);
1209
						} else {
1210
							dataArray.push(data);
1211
						}
1212
					});
1213
1214
					proxyXHR.statusText = "";
1215
1216
					if( _complete ){
1217
						_nextFile.call(_this);
1218
					}
1219
				};
1220
1221
1222
				// Removes file from queue by file reference and returns it
1223
				proxyXHR.remove = function (file) {
1224
				    var i = dataArray.length, _file;
1225
				    while( i-- ){
1226
						if( dataArray[i].file == file ){
1227
							_file = dataArray.splice(i, 1);
1228
							_total -= _file.size;
0 ignored issues
show
Bug introduced by
The variable _total is changed as part of the while loop for example by _file.size on line 1228. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1229
						}
1230
					}
1231
					return	_file;
1232
				};
1233
1234
				return proxyXHR;
1235
			},
1236
1237
1238
			_getFilesDataArray: function (data){
1239
				var files = [], oFiles = {};
1240
1241
				if( isInputFile(data) ){
1242
					var tmp = api.getFiles(data);
1243
					oFiles[data.name || 'file'] = data.getAttribute('multiple') !== null ? tmp : tmp[0];
1244
				}
1245
				else if( _isArray(data) && isInputFile(data[0]) ){
1246
					_each(data, function (input){
1247
						oFiles[input.name || 'file'] = api.getFiles(input);
1248
					});
1249
				}
1250
				else {
1251
					oFiles = data;
1252
				}
1253
1254
				_each(oFiles, function add(file, name){
1255
					if( _isArray(file) ){
1256
						_each(file, function (file){
1257
							add(file, name);
1258
						});
1259
					}
1260
					else if( file && (file.name || file.image) ){
1261
						files.push({
1262
							  name: name
1263
							, file: file
1264
							, size: file.size
1265
							, total: file.size
1266
							, loaded: 0
1267
						});
1268
					}
1269
				});
1270
1271
				if( !files.length ){
1272
					// Create fake `file` object
1273
					files.push({ file: { name: api.expando } });
1274
				}
1275
1276
				return	files;
1277
			},
1278
1279
1280
			_getFormData: function (options, data, fn){
1281
				var
1282
					  file = data.file
1283
					, name = data.name
1284
					, filename = file.name
1285
					, filetype = file.type
1286
					, trans = api.support.transform && options.imageTransform
1287
					, Form = new api.Form
1288
					, queue = api.queue(function (){ fn(Form); })
1289
					, isOrignTrans = trans && _isOriginTransform(trans)
1290
					, postNameConcat = api.postNameConcat
1291
				;
1292
1293
				// Append data
1294
				_each(options.data, function add(val, name){
1295
					if( typeof val == 'object' ){
1296
						_each(val, function (v, i){
1297
							add(v, postNameConcat(name, i));
1298
						});
1299
					}
1300
					else {
1301
						Form.append(name, val);
1302
					}
1303
				});
1304
1305
				(function _addFile(file/**Object*/){
1306
					if( file.image ){ // This is a FileAPI.Image
1307
						queue.inc();
1308
1309
						file.toData(function (err, image){
1310
							// @todo: error
1311
							filename = filename || (new Date).getTime()+'.png';
1312
1313
							_addFile(image);
1314
							queue.next();
1315
						});
1316
					}
1317
					else if( api.Image && trans && (/^image/.test(file.type) || _rimgcanvas.test(file.nodeName)) ){
1318
						queue.inc();
1319
1320
						if( isOrignTrans ){
1321
							// Convert to array for transform function
1322
							trans = [trans];
1323
						}
1324
1325
						api.Image.transform(file, trans, options.imageAutoOrientation, function (err, images){
1326
							if( isOrignTrans && !err ){
1327
								if( !dataURLtoBlob && !api.flashEngine ){
1328
									// Canvas.toBlob or Flash not supported, use multipart
1329
									Form.multipart = true;
1330
								}
1331
1332
								Form.append(name, images[0], filename,  trans[0].type || filetype);
1333
							}
1334
							else {
1335
								var addOrigin = 0;
1336
1337
								if( !err ){
1338
									_each(images, function (image, idx){
1339
										if( !dataURLtoBlob && !api.flashEngine ){
1340
											Form.multipart = true;
1341
										}
1342
1343
										if( !trans[idx].postName ){
1344
											addOrigin = 1;
1345
										}
1346
1347
										Form.append(trans[idx].postName || postNameConcat(name, idx), image, filename, trans[idx].type || filetype);
1348
									});
1349
								}
1350
1351
								if( err || options.imageOriginal ){
1352
									Form.append(postNameConcat(name, (addOrigin ? 'original' : null)), file, filename, filetype);
1353
								}
1354
							}
1355
1356
							queue.next();
1357
						});
1358
					}
1359
					else if( filename !== api.expando ){
1360
						Form.append(name, file, filename);
1361
					}
1362
				})(file);
1363
1364
				queue.check();
1365
			},
1366
1367
1368
			reset: function (inp, notRemove){
1369
				var parent, clone;
1370
1371
				if( jQuery ){
1372
					clone = jQuery(inp).clone(true).insertBefore(inp).val('')[0];
1373
					if( !notRemove ){
1374
						jQuery(inp).remove();
1375
					}
1376
				} else {
1377
					parent  = inp.parentNode;
1378
					clone   = parent.insertBefore(inp.cloneNode(true), inp);
1379
					clone.value = '';
1380
1381
					if( !notRemove ){
1382
						parent.removeChild(inp);
1383
					}
1384
1385
					_each(_elEvents[api.uid(inp)], function (fns, type){
1386
						_each(fns, function (fn){
1387
							_off(inp, type, fn);
1388
							_on(clone, type, fn);
1389
						});
1390
					});
1391
				}
1392
1393
				return  clone;
1394
			},
1395
1396
1397
			/**
1398
			 * Load remote file
1399
			 *
1400
			 * @param   {String}    url
1401
			 * @param   {Function}  fn
1402
			 * @return  {XMLHttpRequest}
1403
			 */
1404
			load: function (url, fn){
1405
				var xhr = api.getXHR();
1406
				if( xhr ){
1407
					xhr.open('GET', url, true);
1408
1409
					if( xhr.overrideMimeType ){
1410
				        xhr.overrideMimeType('text/plain; charset=x-user-defined');
1411
					}
1412
1413
					_on(xhr, 'progress', function (/**Event*/evt){
1414
						/** @namespace evt.lengthComputable */
1415
						if( evt.lengthComputable ){
1416
							fn({ type: evt.type, loaded: evt.loaded, total: evt.total }, xhr);
1417
						}
1418
					});
1419
1420
					xhr.onreadystatechange = function(){
1421
						if( xhr.readyState == 4 ){
1422
							xhr.onreadystatechange = null;
1423
							if( xhr.status == 200 ){
1424
								url = url.split('/');
1425
								/** @namespace xhr.responseBody */
1426
								var file = {
1427
								      name: url[url.length-1]
1428
									, size: xhr.getResponseHeader('Content-Length')
1429
									, type: xhr.getResponseHeader('Content-Type')
1430
								};
1431
								file.dataURL = 'data:'+file.type+';base64,' + api.encode64(xhr.responseBody || xhr.responseText);
1432
								fn({ type: 'load', result: file }, xhr);
1433
							}
1434
							else {
1435
								fn({ type: 'error' }, xhr);
1436
							}
1437
					    }
1438
					};
1439
				    xhr.send(null);
1440
				} else {
1441
					fn({ type: 'error' });
1442
				}
1443
1444
				return  xhr;
1445
			},
1446
1447
			encode64: function (str){
1448
				var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', outStr = '', i = 0;
1449
1450
				if( typeof str !== 'string' ){
1451
					str	= String(str);
1452
				}
1453
1454
				while( i < str.length ){
1455
					//all three "& 0xff" added below are there to fix a known bug
1456
					//with bytes returned by xhr.responseText
1457
					var
1458
						  byte1 = str.charCodeAt(i++) & 0xff
1459
						, byte2 = str.charCodeAt(i++) & 0xff
1460
						, byte3 = str.charCodeAt(i++) & 0xff
1461
						, enc1 = byte1 >> 2
1462
						, enc2 = ((byte1 & 3) << 4) | (byte2 >> 4)
1463
						, enc3, enc4
1464
					;
1465
1466
					if( isNaN(byte2) ){
1467
						enc3 = enc4 = 64;
1468
					} else {
1469
						enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
1470
						enc4 = isNaN(byte3) ? 64 : byte3 & 63;
1471
					}
1472
1473
					outStr += b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);
1474
				}
1475
1476
				return  outStr;
1477
			}
1478
1479
		} // api
1480
	;
1481
1482
1483
	function _emit(target, fn, name, res, ext){
1484
		var evt = {
1485
			  type:		name.type || name
1486
			, target:	target
1487
			, result:	res
1488
		};
1489
		_extend(evt, ext);
1490
		fn(evt);
1491
	}
1492
1493
1494
	function _hasSupportReadAs(as){
1495
		return	FileReader && !!FileReader.prototype['readAs'+as];
1496
	}
1497
1498
1499
	function _readAs(file, fn, as, encoding){
1500
		if( api.isBlob(file) && _hasSupportReadAs(as) ){
1501
			var Reader = new FileReader;
1502
1503
			// Add event listener
1504
			_on(Reader, _readerEvents, function _fn(evt){
1505
				var type = evt.type;
1506
				if( type == 'progress' ){
1507
					_emit(file, fn, evt, evt.target.result, { loaded: evt.loaded, total: evt.total });
1508
				}
1509
				else if( type == 'loadend' ){
1510
					_off(Reader, _readerEvents, _fn);
1511
					Reader = null;
1512
				}
1513
				else {
1514
					_emit(file, fn, evt, evt.target.result);
1515
				}
1516
			});
1517
1518
1519
			try {
1520
				// ReadAs ...
1521
				if( encoding ){
1522
					Reader['readAs'+as](file, encoding);
1523
				}
1524
				else {
1525
					Reader['readAs'+as](file);
1526
				}
1527
			}
1528
			catch (err){
1529
				_emit(file, fn, 'error', undef, { error: err.toString() });
1530
			}
1531
		}
1532
		else {
1533
			_emit(file, fn, 'error', undef, { error: 'FileReader_not_support_'+as });
1534
		}
1535
	}
1536
1537
1538
	function _isRegularFile(file, callback){
1539
		// http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects
1540
		if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){
1541
			if( FileReader ){
1542
				try {
1543
					var Reader = new FileReader();
1544
1545
					_one(Reader, _readerEvents, function (evt){
1546
						var isFile = evt.type != 'error';
1547
						callback(isFile);
1548
						if( isFile ){
1549
							Reader.abort();
1550
						}
1551
					});
1552
1553
					Reader.readAsDataURL(file);
1554
				} catch( err ){
1555
					callback(false);
1556
				}
1557
			}
1558
			else {
1559
				callback(null);
1560
			}
1561
		}
1562
		else {
1563
			callback(true);
1564
		}
1565
	}
1566
1567
1568
	function _getAsEntry(item){
1569
		var entry;
1570
		if( item.getAsEntry ){ entry = item.getAsEntry(); }
1571
		else if( item.webkitGetAsEntry ){ entry = item.webkitGetAsEntry(); }
1572
		return	entry;
1573
	}
1574
1575
1576
	function _readEntryAsFiles(entry, callback){
1577
		if( !entry ){
1578
			// error
1579
			callback('invalid entry');
1580
		}
1581
		else if( entry.isFile ){
1582
			// Read as file
1583
			entry.file(function(file){
1584
				// success
1585
				file.fullPath = entry.fullPath;
1586
				callback(false, [file]);
1587
			}, function (err){
1588
				// error
1589
				callback('FileError.code: '+err.code);
1590
			});
1591
		}
1592
		else if( entry.isDirectory ){
1593
			var reader = entry.createReader(), result = [];
1594
1595
			reader.readEntries(function(entries){
1596
				// success
1597
				api.afor(entries, function (next, entry){
1598
					_readEntryAsFiles(entry, function (err, files){
1599
						if( err ){
1600
							api.log(err);
1601
						}
1602
						else {
1603
							result = result.concat(files);
1604
						}
1605
1606
						if( next ){
1607
							next();
1608
						}
1609
						else {
1610
							callback(false, result);
1611
						}
1612
					});
1613
				});
1614
			}, function (err){
1615
				// error
1616
				callback('directory_reader: ' + err);
1617
			});
1618
		}
1619
		else {
1620
			_readEntryAsFiles(_getAsEntry(entry), callback);
1621
		}
1622
	}
1623
1624
1625
	function _simpleClone(obj){
1626
		var copy = {};
1627
		_each(obj, function (val, key){
1628
			if( val && (typeof val === 'object') && (val.nodeType === void 0) ){
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1629
				val = _extend({}, val);
1630
			}
1631
			copy[key] = val;
1632
		});
1633
		return	copy;
1634
	}
1635
1636
1637
	function isInputFile(el){
1638
		return	_rinput.test(el && el.tagName);
1639
	}
1640
1641
1642
	function _getDataTransfer(evt){
1643
		return	(evt.originalEvent || evt || '').dataTransfer || {};
1644
	}
1645
1646
1647
	function _isOriginTransform(trans){
1648
		var key;
1649
		for( key in trans ){
1650
			if( trans.hasOwnProperty(key) ){
1651
				if( !(trans[key] instanceof Object || key === 'overlay' || key === 'filter') ){
1652
					return	true;
1653
				}
1654
			}
1655
		}
1656
		return	false;
1657
	}
1658
1659
1660
	// Add default image info reader
1661
	api.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){
1662
		if( !file.__dimensions ){
1663
			var defer = file.__dimensions = api.defer();
1664
1665
			api.readAsImage(file, function (evt){
1666
				var img = evt.target;
1667
				defer.resolve(evt.type == 'load' ? false : 'error', {
1668
					  width:  img.width
1669
					, height: img.height
1670
				});
1671
                img.src = api.EMPTY_PNG;
1672
				img = null;
0 ignored issues
show
Unused Code introduced by
The assignment to img seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
1673
			});
1674
		}
1675
1676
		file.__dimensions.then(callback);
1677
	});
1678
1679
1680
	/**
1681
	 * Drag'n'Drop special event
1682
	 *
1683
	 * @param	{HTMLElement}	el
1684
	 * @param	{Function}		onHover
1685
	 * @param	{Function}		onDrop
1686
	 */
1687
	api.event.dnd = function (el, onHover, onDrop){
1688
		var _id, _type;
1689
1690
		if( !onDrop ){
1691
			onDrop = onHover;
1692
			onHover = api.F;
1693
		}
1694
1695
		if( FileReader ){
1696
			// Hover
1697
			_on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
1698
				var
1699
					  types = _getDataTransfer(evt).types
1700
					, i = types && types.length
1701
					, debounceTrigger = false
1702
				;
1703
1704
				while( i-- ){
1705
					if( ~types[i].indexOf('File') ){
1706
						evt[preventDefault]();
1707
1708
						if( _type !== evt.type ){
1709
							_type = evt.type; // Store current type of event
1710
1711
							if( _type != 'dragleave' ){
0 ignored issues
show
Bug introduced by
The variable _type is changed as part of the while loop for example by evt.type on line 1709. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1712
								onHover.call(evt[currentTarget], true, evt);
1713
							}
1714
1715
							debounceTrigger = true;
1716
						}
1717
1718
						break; // exit from "while"
1719
					}
1720
				}
1721
1722
				if( debounceTrigger ){
1723
					clearTimeout(_id);
1724
					_id = setTimeout(function (){
1725
						onHover.call(evt[currentTarget], _type != 'dragleave', evt);
1726
					}, 50);
1727
				}
1728
			});
1729
1730
1731
			// Drop
1732
			_on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){
1733
				evt[preventDefault]();
1734
1735
				_type = 0;
1736
				onHover.call(evt[currentTarget], false, evt);
1737
1738
				api.getDropFiles(evt, function (files){
1739
					onDrop.call(evt[currentTarget], files, evt);
1740
				});
1741
			});
1742
		}
1743
		else {
1744
			api.log("Drag'n'Drop -- not supported");
1745
		}
1746
	};
1747
1748
1749
	/**
1750
	 * Remove drag'n'drop
1751
	 * @param	{HTMLElement}	el
1752
	 * @param	{Function}		onHover
1753
	 * @param	{Function}		onDrop
1754
	 */
1755
	api.event.dnd.off = function (el, onHover, onDrop){
1756
		_off(el, 'dragenter dragleave dragover', onHover.ff);
1757
		_off(el, 'drop', onDrop.ff);
1758
	};
1759
1760
1761
	// Support jQuery
1762
	if( jQuery && !jQuery.fn.dnd ){
1763
		jQuery.fn.dnd = function (onHover, onDrop){
1764
			return this.each(function (){
1765
				api.event.dnd(this, onHover, onDrop);
1766
			});
1767
		};
1768
1769
		jQuery.fn.offdnd = function (onHover, onDrop){
1770
			return this.each(function (){
1771
				api.event.dnd.off(this, onHover, onDrop);
1772
			});
1773
		};
1774
	}
1775
1776
	// @export
1777
	window.FileAPI  = _extend(api, window.FileAPI);
1778
1779
1780
	// Debug info
1781
	api.log('FileAPI: ' + api.version);
1782
	api.log('protocol: ' + window.location.protocol);
1783
	api.log('doctype: [' + doctype.name + '] ' + doctype.publicId + ' ' + doctype.systemId);
1784
1785
1786
	// @detect 'x-ua-compatible'
1787
	_each(document.getElementsByTagName('meta'), function (meta){
1788
		if( /x-ua-compatible/i.test(meta.getAttribute('http-equiv')) ){
1789
			api.log('meta.http-equiv: ' + meta.getAttribute('content'));
1790
		}
1791
	});
1792
1793
1794
	// configuration
1795
	try {
1796
		api._supportConsoleLog = !!console.log;
1797
		api._supportConsoleLogApply = !!console.log.apply;
1798
	} catch (err) {}
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...
1799
1800
	if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
1801
	if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
1802
	if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
1803
})(window, void 0);
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1804
1805
/*global window, FileAPI, document */
1806
1807
(function (api, document, undef) {
1808
	'use strict';
1809
1810
	var
1811
		min = Math.min,
1812
		round = Math.round,
1813
		getCanvas = function () { return document.createElement('canvas'); },
1814
		support = false,
1815
		exifOrientation = {
1816
			  8:	270
1817
			, 3:	180
1818
			, 6:	90
1819
			, 7:	270
1820
			, 4:	180
1821
			, 5:	90
1822
		}
1823
	;
1824
1825
	try {
1826
		support = getCanvas().toDataURL('image/png').indexOf('data:image/png') > -1;
1827
	}
1828
	catch (e){}
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...
1829
1830
1831
	function Image(file){
1832
		if( file instanceof Image ){
1833
			var img = new Image(file.file);
1834
			api.extend(img.matrix, file.matrix);
1835
			return	img;
1836
		}
1837
		else if( !(this instanceof Image) ){
1838
			return	new Image(file);
1839
		}
1840
1841
		this.file   = file;
1842
		this.size   = file.size || 100;
1843
1844
		this.matrix	= {
1845
			sx: 0,
1846
			sy: 0,
1847
			sw: 0,
1848
			sh: 0,
1849
			dx: 0,
1850
			dy: 0,
1851
			dw: 0,
1852
			dh: 0,
1853
			resize: 0, // min, max OR preview
1854
			deg: 0,
1855
			quality: 1, // jpeg quality
1856
			filter: 0
1857
		};
1858
	}
1859
1860
1861
	Image.prototype = {
1862
		image: true,
1863
		constructor: Image,
1864
1865
		set: function (attrs){
1866
			api.extend(this.matrix, attrs);
1867
			return	this;
1868
		},
1869
1870
		crop: function (x, y, w, h){
1871
			if( w === undef ){
1872
				w	= x;
1873
				h	= y;
1874
				x = y = 0;
1875
			}
1876
			return	this.set({ sx: x, sy: y, sw: w, sh: h || w });
1877
		},
1878
1879
		resize: function (w, h, strategy){
1880
			if( /min|max/.test(h) ){
1881
				strategy = h;
1882
				h = w;
1883
			}
1884
1885
			return	this.set({ dw: w, dh: h || w, resize: strategy });
1886
		},
1887
1888
		preview: function (w, h){
1889
			return	this.resize(w, h || w, 'preview');
1890
		},
1891
1892
		rotate: function (deg){
1893
			return	this.set({ deg: deg });
1894
		},
1895
1896
		filter: function (filter){
1897
			return	this.set({ filter: filter });
1898
		},
1899
1900
		overlay: function (images){
1901
			return	this.set({ overlay: images });
1902
		},
1903
1904
		clone: function (){
1905
			return	new Image(this);
1906
		},
1907
1908
		_load: function (image, fn){
1909
			var self = this;
1910
1911
			if( /img|video/i.test(image.nodeName) ){
1912
				fn.call(self, null, image);
1913
			}
1914
			else {
1915
				api.readAsImage(image, function (evt){
1916
					fn.call(self, evt.type != 'load', evt.result);
1917
				});
1918
			}
1919
		},
1920
1921
		_apply: function (image, fn){
1922
			var
1923
				  canvas = getCanvas()
1924
				, m = this.getMatrix(image)
1925
				, ctx = canvas.getContext('2d')
1926
				, width = image.videoWidth || image.width
1927
				, height = image.videoHeight || image.height
1928
				, deg = m.deg
1929
				, dw = m.dw
1930
				, dh = m.dh
1931
				, w = width
1932
				, h = height
1933
				, filter = m.filter
1934
				, copy // canvas copy
1935
				, buffer = image
1936
				, overlay = m.overlay
1937
				, queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
1938
				, renderImageToCanvas = api.renderImageToCanvas
1939
			;
1940
1941
			// Normalize angle
1942
			deg = deg - Math.floor(deg/360)*360;
1943
1944
			// For `renderImageToCanvas`
1945
			image._type = this.file.type;
1946
1947
			while(m.multipass && min(w/dw, h/dh) > 2 ){
1948
				w = (w/2 + 0.5)|0;
1949
				h = (h/2 + 0.5)|0;
1950
1951
				copy = getCanvas();
1952
				copy.width  = w;
1953
				copy.height = h;
1954
1955
				if( buffer !== image ){
1956
					renderImageToCanvas(copy, buffer, 0, 0, buffer.width, buffer.height, 0, 0, w, h);
1957
					buffer = copy;
1958
				}
1959
				else {
1960
					buffer = copy;
1961
					renderImageToCanvas(buffer, image, m.sx, m.sy, m.sw, m.sh, 0, 0, w, h);
1962
					m.sx = m.sy = m.sw = m.sh = 0;
1963
				}
1964
			}
1965
1966
1967
			canvas.width  = (deg % 180) ? dh : dw;
1968
			canvas.height = (deg % 180) ? dw : dh;
1969
1970
			canvas.type = m.type;
1971
			canvas.quality = m.quality;
1972
1973
			ctx.rotate(deg * Math.PI / 180);
1974
			renderImageToCanvas(ctx.canvas, buffer
1975
				, m.sx, m.sy
1976
				, m.sw || buffer.width
1977
				, m.sh || buffer.height
1978
				, (deg == 180 || deg == 270 ? -dw : 0)
1979
				, (deg == 90 || deg == 180 ? -dh : 0)
1980
				, dw, dh
1981
			);
1982
			dw = canvas.width;
1983
			dh = canvas.height;
1984
1985
			// Apply overlay
1986
			overlay && api.each([].concat(overlay), function (over){
1987
				queue.inc();
1988
				// preload
1989
				var img = new window.Image, fn = function (){
1990
					var
1991
						  x = over.x|0
1992
						, y = over.y|0
1993
						, w = over.w || img.width
1994
						, h = over.h || img.height
1995
						, rel = over.rel
1996
					;
1997
1998
					// center  |  right  |  left
1999
					x = (rel == 1 || rel == 4 || rel == 7) ? (dw - w + x)/2 : (rel == 2 || rel == 5 || rel == 8 ? dw - (w + x) : x);
2000
2001
					// center  |  bottom  |  top
2002
					y = (rel == 3 || rel == 4 || rel == 5) ? (dh - h + y)/2 : (rel >= 6 ? dh - (h + y) : y);
2003
2004
					api.event.off(img, 'error load abort', fn);
2005
2006
					try {
2007
						ctx.globalAlpha = over.opacity || 1;
2008
						ctx.drawImage(img, x, y, w, h);
2009
					}
2010
					catch (er){}
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...
2011
2012
					queue.next();
2013
				};
2014
2015
				api.event.on(img, 'error load abort', fn);
2016
				img.src = over.src;
2017
2018
				if( img.complete ){
2019
					fn();
2020
				}
2021
			});
2022
2023
			if( filter ){
2024
				queue.inc();
2025
				Image.applyFilter(canvas, filter, queue.next);
2026
			}
2027
2028
			queue.check();
2029
		},
2030
2031
		getMatrix: function (image){
2032
			var
2033
				  m  = api.extend({}, this.matrix)
2034
				, sw = m.sw = m.sw || image.videoWidth || image.naturalWidth ||  image.width
2035
				, sh = m.sh = m.sh || image.videoHeight || image.naturalHeight || image.height
2036
				, dw = m.dw = m.dw || sw
2037
				, dh = m.dh = m.dh || sh
2038
				, sf = sw/sh, df = dw/dh
2039
				, strategy = m.resize
2040
			;
2041
2042
			if( strategy == 'preview' ){
2043
				if( dw != sw || dh != sh ){
2044
					// Make preview
2045
					var w, h;
2046
2047
					if( df >= sf ){
2048
						w	= sw;
2049
						h	= w / df;
2050
					} else {
2051
						h	= sh;
2052
						w	= h * df;
2053
					}
2054
2055
					if( w != sw || h != sh ){
2056
						m.sx	= ~~((sw - w)/2);
2057
						m.sy	= ~~((sh - h)/2);
2058
						sw		= w;
2059
						sh		= h;
2060
					}
2061
				}
2062
			}
2063
			else if( strategy ){
2064
				if( !(sw > dw || sh > dh) ){
2065
					dw = sw;
2066
					dh = sh;
2067
				}
2068
				else if( strategy == 'min' ){
2069
					dw = round(sf < df ? min(sw, dw) : dh*sf);
2070
					dh = round(sf < df ? dw/sf : min(sh, dh));
2071
				}
2072
				else {
2073
					dw = round(sf >= df ? min(sw, dw) : dh*sf);
2074
					dh = round(sf >= df ? dw/sf : min(sh, dh));
2075
				}
2076
			}
2077
2078
			m.sw = sw;
2079
			m.sh = sh;
2080
			m.dw = dw;
2081
			m.dh = dh;
2082
			m.multipass = api.multiPassResize;
2083
			return	m;
2084
		},
2085
2086
		_trans: function (fn){
2087
			this._load(this.file, function (err, image){
2088
				if( err ){
2089
					fn(err);
2090
				}
2091
				else {
2092
					try {
2093
						this._apply(image, fn);
2094
					} catch (err){
2095
						api.log('[err] FileAPI.Image.fn._apply:', err);
2096
						fn(err);
2097
					}
2098
				}
2099
			});
2100
		},
2101
2102
2103
		get: function (fn){
2104
			if( api.support.transform ){
2105
				var _this = this, matrix = _this.matrix;
2106
2107
				if( matrix.deg == 'auto' ){
2108
					api.getInfo(_this.file, function (err, info){
2109
						// rotate by exif orientation
2110
						matrix.deg = exifOrientation[info && info.exif && info.exif.Orientation] || 0;
2111
						_this._trans(fn);
2112
					});
2113
				}
2114
				else {
2115
					_this._trans(fn);
2116
				}
2117
			}
2118
			else {
2119
				fn('not_support_transform');
2120
			}
2121
2122
			return this;
2123
		},
2124
2125
2126
		toData: function (fn){
2127
			return this.get(fn);
2128
		}
2129
2130
	};
2131
2132
2133
	Image.exifOrientation = exifOrientation;
2134
2135
2136
	Image.transform = function (file, transform, autoOrientation, fn){
2137
		function _transform(err, img){
2138
			// img -- info object
2139
			var
2140
				  images = {}
2141
				, queue = api.queue(function (err){
2142
					fn(err, images);
2143
				})
2144
			;
2145
2146
			if( !err ){
2147
				api.each(transform, function (params, name){
2148
					if( !queue.isFail() ){
2149
						var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
2150
2151
						if( isFn ){
2152
							params(img, ImgTrans);
2153
						}
2154
						else if( params.width ){
2155
							ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
2156
						}
2157
						else {
2158
							if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
2159
								ImgTrans.resize(params.maxWidth, params.maxHeight, 'max');
2160
							}
2161
						}
2162
2163
						if( params.crop ){
2164
							var crop = params.crop;
2165
							ImgTrans.crop(crop.x|0, crop.y|0, crop.w || crop.width, crop.h || crop.height);
2166
						}
2167
2168
						if( params.rotate === undef && autoOrientation ){
2169
							params.rotate = 'auto';
2170
						}
2171
2172
						ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
2173
2174
						if( !isFn ){
2175
							ImgTrans.set({
2176
								  deg: params.rotate
2177
								, overlay: params.overlay
2178
								, filter: params.filter
2179
								, quality: params.quality || 1
2180
							});
2181
						}
2182
2183
						queue.inc();
2184
						ImgTrans.toData(function (err, image){
2185
							if( err ){
2186
								queue.fail();
2187
							}
2188
							else {
2189
								images[name] = image;
2190
								queue.next();
2191
							}
2192
						});
2193
					}
2194
				});
2195
			}
2196
			else {
2197
				queue.fail();
2198
			}
2199
		}
2200
2201
2202
		// @todo: Оло-ло, нужно рефакторить это место
2203
		if( file.width ){
2204
			_transform(false, file);
2205
		} else {
2206
			api.getInfo(file, _transform);
2207
		}
2208
	};
2209
2210
2211
	// @const
2212
	api.each(['TOP', 'CENTER', 'BOTTOM'], function (x, i){
2213
		api.each(['LEFT', 'CENTER', 'RIGHT'], function (y, j){
2214
			Image[x+'_'+y] = i*3 + j;
2215
			Image[y+'_'+x] = i*3 + j;
2216
		});
2217
	});
2218
2219
2220
	/**
2221
	 * Trabsform element to canvas
2222
	 *
2223
	 * @param    {Image|HTMLVideoElement}   el
2224
	 * @returns  {Canvas}
2225
	 */
2226
	Image.toCanvas = function(el){
2227
		var canvas		= document.createElement('canvas');
2228
		canvas.width	= el.videoWidth || el.width;
2229
		canvas.height	= el.videoHeight || el.height;
2230
		canvas.getContext('2d').drawImage(el, 0, 0);
2231
		return	canvas;
2232
	};
2233
2234
2235
	/**
2236
	 * Create image from DataURL
2237
	 * @param  {String}  dataURL
2238
	 * @param  {Object}  size
2239
	 * @param  {Function}  callback
2240
	 */
2241
	Image.fromDataURL = function (dataURL, size, callback){
2242
		var img = api.newImage(dataURL);
2243
		api.extend(img, size);
2244
		callback(img);
2245
	};
2246
2247
2248
	/**
2249
	 * Apply filter (caman.js)
2250
	 *
2251
	 * @param  {Canvas|Image}   canvas
2252
	 * @param  {String|Function}  filter
2253
	 * @param  {Function}  doneFn
2254
	 */
2255
	Image.applyFilter = function (canvas, filter, doneFn){
2256
		if( typeof filter == 'function' ){
2257
			filter(canvas, doneFn);
2258
		}
2259
		else if( window.Caman ){
2260
			// http://camanjs.com/guides/
2261
			window.Caman(canvas.tagName == 'IMG' ? Image.toCanvas(canvas) : canvas, function (){
2262
				if( typeof filter == 'string' ){
2263
					this[filter]();
2264
				}
2265
				else {
2266
					api.each(filter, function (val, method){
2267
						this[method](val);
2268
					}, this);
2269
				}
2270
				this.render(doneFn);
2271
			});
2272
		}
2273
	};
2274
2275
2276
	/**
2277
	 * For load-image-ios.js
2278
	 */
2279
	api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
2280
		try {
2281
			return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
2282
		} catch (ex) {
2283
			api.log('renderImageToCanvas failed');
2284
			throw ex;
2285
		}
2286
	};
2287
2288
2289
	// @export
2290
	api.support.canvas = api.support.transform = support;
2291
	api.Image = Image;
2292
})(FileAPI, document);
2293
2294
/*
2295
 * JavaScript Load Image iOS scaling fixes 1.0.3
2296
 * https://github.com/blueimp/JavaScript-Load-Image
2297
 *
2298
 * Copyright 2013, Sebastian Tschan
2299
 * https://blueimp.net
2300
 *
2301
 * iOS image scaling fixes based on
2302
 * https://github.com/stomita/ios-imagefile-megapixel
2303
 *
2304
 * Licensed under the MIT license:
2305
 * http://www.opensource.org/licenses/MIT
2306
 */
2307
2308
/*jslint nomen: true, bitwise: true */
2309
/*global FileAPI, window, document */
2310
2311
(function (factory) {
2312
	'use strict';
2313
	factory(FileAPI);
2314
}(function (loadImage) {
2315
    'use strict';
2316
2317
    // Only apply fixes on the iOS platform:
2318
    if (!window.navigator || !window.navigator.platform ||
2319
             !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
2320
        return;
2321
    }
2322
2323
    var originalRenderMethod = loadImage.renderImageToCanvas;
2324
2325
    // Detects subsampling in JPEG images:
2326
    loadImage.detectSubsampling = function (img) {
2327
        var canvas,
2328
            context;
2329
        if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
2330
            canvas = document.createElement('canvas');
2331
            canvas.width = canvas.height = 1;
2332
            context = canvas.getContext('2d');
2333
            context.drawImage(img, -img.width + 1, 0);
2334
            // subsampled image becomes half smaller in rendering size.
2335
            // check alpha channel value to confirm image is covering edge pixel or not.
2336
            // if alpha value is 0 image is not covering, hence subsampled.
2337
            return context.getImageData(0, 0, 1, 1).data[3] === 0;
2338
        }
2339
        return false;
2340
    };
2341
2342
    // Detects vertical squash in JPEG images:
2343
    loadImage.detectVerticalSquash = function (img, subsampled) {
2344
        var naturalHeight = img.naturalHeight || img.height,
2345
            canvas = document.createElement('canvas'),
2346
            context = canvas.getContext('2d'),
2347
            data,
2348
            sy,
2349
            ey,
2350
            py,
2351
            alpha;
2352
        if (subsampled) {
2353
            naturalHeight /= 2;
2354
        }
2355
        canvas.width = 1;
2356
        canvas.height = naturalHeight;
2357
        context.drawImage(img, 0, 0);
2358
        data = context.getImageData(0, 0, 1, naturalHeight).data;
2359
        // search image edge pixel position in case it is squashed vertically:
2360
        sy = 0;
2361
        ey = naturalHeight;
2362
        py = naturalHeight;
2363
        while (py > sy) {
2364
            alpha = data[(py - 1) * 4 + 3];
2365
            if (alpha === 0) {
2366
                ey = py;
2367
            } else {
2368
                sy = py;
2369
            }
2370
            py = (ey + sy) >> 1;
2371
        }
2372
        return (py / naturalHeight) || 1;
2373
    };
2374
2375
    // Renders image to canvas while working around iOS image scaling bugs:
2376
    // https://github.com/blueimp/JavaScript-Load-Image/issues/13
2377
    loadImage.renderImageToCanvas = function (
2378
        canvas,
2379
        img,
2380
        sourceX,
2381
        sourceY,
2382
        sourceWidth,
2383
        sourceHeight,
2384
        destX,
2385
        destY,
2386
        destWidth,
2387
        destHeight
2388
    ) {
2389
        if (img._type === 'image/jpeg') {
2390
            var context = canvas.getContext('2d'),
2391
                tmpCanvas = document.createElement('canvas'),
2392
                tileSize = 1024,
2393
                tmpContext = tmpCanvas.getContext('2d'),
2394
                subsampled,
2395
                vertSquashRatio,
2396
                tileX,
2397
                tileY;
2398
            tmpCanvas.width = tileSize;
2399
            tmpCanvas.height = tileSize;
2400
            context.save();
2401
            subsampled = loadImage.detectSubsampling(img);
2402
            if (subsampled) {
2403
                sourceX /= 2;
2404
                sourceY /= 2;
2405
                sourceWidth /= 2;
2406
                sourceHeight /= 2;
2407
            }
2408
            vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
2409
            if (subsampled || vertSquashRatio !== 1) {
2410
                sourceY *= vertSquashRatio;
2411
                destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
2412
                destHeight = Math.ceil(
2413
                    tileSize * destHeight / sourceHeight / vertSquashRatio
2414
                );
2415
                destY = 0;
2416
                tileY = 0;
2417
                while (tileY < sourceHeight) {
2418
                    destX = 0;
2419
                    tileX = 0;
2420
                    while (tileX < sourceWidth) {
2421
                        tmpContext.clearRect(0, 0, tileSize, tileSize);
2422
                        tmpContext.drawImage(
2423
                            img,
2424
                            sourceX,
2425
                            sourceY,
2426
                            sourceWidth,
2427
                            sourceHeight,
2428
                            -tileX,
2429
                            -tileY,
2430
                            sourceWidth,
2431
                            sourceHeight
2432
                        );
2433
                        context.drawImage(
2434
                            tmpCanvas,
2435
                            0,
2436
                            0,
2437
                            tileSize,
2438
                            tileSize,
2439
                            destX,
2440
                            destY,
2441
                            destWidth,
2442
                            destHeight
2443
                        );
2444
                        tileX += tileSize;
2445
                        destX += destWidth;
2446
                    }
2447
                    tileY += tileSize;
2448
                    destY += destHeight;
2449
                }
2450
                context.restore();
2451
                return canvas;
2452
            }
2453
        }
2454
        return originalRenderMethod(
2455
            canvas,
2456
            img,
2457
            sourceX,
2458
            sourceY,
2459
            sourceWidth,
2460
            sourceHeight,
2461
            destX,
2462
            destY,
2463
            destWidth,
2464
            destHeight
2465
        );
2466
    };
2467
2468
}));
2469
2470
/*global window, FileAPI */
2471
2472
(function (api, window){
2473
	"use strict";
2474
2475
	var
2476
		  document = window.document
2477
		, FormData = window.FormData
2478
		, Form = function (){ this.items = []; }
2479
		, encodeURIComponent = window.encodeURIComponent
2480
	;
2481
2482
2483
	Form.prototype = {
2484
2485
		append: function (name, blob, file, type){
2486
			this.items.push({
2487
				  name: name
2488
				, blob: blob && blob.blob || (blob == void 0 ? '' : blob)
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
2489
				, file: blob && (file || blob.name)
2490
				, type:	blob && (type || blob.type)
2491
			});
2492
		},
2493
2494
		each: function (fn){
2495
			var i = 0, n = this.items.length;
2496
			for( ; i < n; i++ ){
2497
				fn.call(this, this.items[i]);
2498
			}
2499
		},
2500
2501
		toData: function (fn, options){
2502
		    // allow chunked transfer if we have only one file to send
2503
		    // flag is used below and in XHR._send
2504
		    options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1;
2505
2506
			if( !api.support.html5 ){
2507
				api.log('FileAPI.Form.toHtmlData');
2508
				this.toHtmlData(fn);
2509
			}
2510
			else if( !api.formData || this.multipart || !FormData ){
2511
				api.log('FileAPI.Form.toMultipartData');
2512
				this.toMultipartData(fn);
2513
			}
2514
			else if( options._chunked ){
2515
				api.log('FileAPI.Form.toPlainData');
2516
				this.toPlainData(fn);
2517
			}
2518
			else {
2519
				api.log('FileAPI.Form.toFormData');
2520
				this.toFormData(fn);
2521
			}
2522
		},
2523
2524
		_to: function (data, complete, next, arg){
2525
			var queue = api.queue(function (){
2526
				complete(data);
2527
			});
2528
2529
			this.each(function (file){
2530
				next(file, data, queue, arg);
2531
			});
2532
2533
			queue.check();
2534
		},
2535
2536
2537
		toHtmlData: function (fn){
2538
			this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){
2539
				var blob = file.blob, hidden;
2540
2541
				if( file.file ){
2542
					api.reset(blob, true);
2543
					// set new name
2544
					blob.name = file.name;
2545
					blob.disabled = false;
2546
					data.appendChild(blob);
2547
				}
2548
				else {
2549
					hidden = document.createElement('input');
2550
					hidden.name  = file.name;
2551
					hidden.type  = 'hidden';
2552
					hidden.value = blob;
2553
					data.appendChild(hidden);
2554
				}
2555
			});
2556
		},
2557
2558
		toPlainData: function (fn){
2559
			this._to({}, fn, function (file, data, queue){
2560
				if( file.file ){
2561
					data.type = file.file;
2562
				}
2563
2564
				if( file.blob.toBlob ){
2565
				    // canvas
2566
					queue.inc();
2567
					_convertFile(file, function (file, blob){
2568
						data.name = file.name;
2569
						data.file = blob;
2570
						data.size = blob.length;
2571
						data.type = file.type;
2572
						queue.next();
2573
					});
2574
				}
2575
				else if( file.file ){
2576
				    // file
2577
					data.name = file.blob.name;
2578
					data.file = file.blob;
2579
					data.size = file.blob.size;
2580
					data.type = file.type;
2581
				}
2582
				else {
2583
				    // additional data
2584
				    if( !data.params ){
2585
				        data.params = [];
2586
				    }
2587
				    data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob));
2588
				}
2589
2590
				data.start = -1;
2591
				data.end = data.file && data.file.FileAPIReadPosition || -1;
2592
				data.retry = 0;
2593
			});
2594
		},
2595
2596
		toFormData: function (fn){
2597
			this._to(new FormData, fn, function (file, data, queue){
2598
				if( file.blob && file.blob.toBlob ){
2599
					queue.inc();
2600
					_convertFile(file, function (file, blob){
2601
						data.append(file.name, blob, file.file);
2602
						queue.next();
2603
					});
2604
				}
2605
				else if( file.file ){
2606
					data.append(file.name, file.blob, file.file);
2607
				}
2608
				else {
2609
					data.append(file.name, file.blob);
2610
				}
2611
2612
				if( file.file ){
2613
					data.append('_'+file.name, file.file);
2614
				}
2615
			});
2616
		},
2617
2618
2619
		toMultipartData: function (fn){
2620
			this._to([], fn, function (file, data, queue, boundary){
2621
				queue.inc();
2622
				_convertFile(file, function (file, blob){
2623
					data.push(
2624
						  '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '')
2625
						+ (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '')
2626
						+ '\r\n'
2627
						+ '\r\n'+ (file.file ? blob : encodeURIComponent(blob))
2628
						+ '\r\n')
2629
					);
2630
					queue.next();
2631
				}, true);
2632
			}, api.expando);
2633
		}
2634
	};
2635
2636
2637
	function _convertFile(file, fn, useBinaryString){
2638
		var blob = file.blob, filename = file.file;
2639
2640
		if( filename ){
2641
			if( !blob.toDataURL ){
2642
				// The Blob is not an image.
2643
				api.readAsBinaryString(blob, function (evt){
2644
					if( evt.type == 'load' ){
2645
						fn(file, evt.result);
2646
					}
2647
				});
2648
				return;
2649
			}
2650
2651
			var
2652
				  mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' }
2653
				, type = mime[file.type] ? file.type : 'image/png'
2654
				, ext  = mime[type] || '.png'
2655
				, quality = blob.quality || 1
2656
			;
2657
2658
			if( !filename.match(new RegExp(ext+'$', 'i')) ){
2659
				// Does not change the current extension, but add a new one.
2660
				filename += ext.replace('?', '');
2661
			}
2662
2663
			file.file = filename;
2664
			file.type = type;
2665
2666
			if( !useBinaryString && blob.toBlob ){
2667
				blob.toBlob(function (blob){
2668
					fn(file, blob);
2669
				}, type, quality);
2670
			}
2671
			else {
2672
				fn(file, api.toBinaryString(blob.toDataURL(type, quality)));
2673
			}
2674
		}
2675
		else {
2676
			fn(file, blob);
2677
		}
2678
	}
2679
2680
2681
	// @export
2682
	api.Form = Form;
2683
})(FileAPI, window);
2684
2685
/*global window, FileAPI, Uint8Array */
2686
2687
(function (window, api){
2688
	"use strict";
2689
2690
	var
2691
		  noop = function (){}
2692
		, document = window.document
2693
2694
		, XHR = function (options){
2695
			this.uid = api.uid();
2696
			this.xhr = {
2697
				  abort: noop
2698
				, getResponseHeader: noop
2699
				, getAllResponseHeaders: noop
2700
			};
2701
			this.options = options;
2702
		},
2703
2704
		_xhrResponsePostfix = { '': 1, XML: 1, Text: 1, Body: 1 }
2705
	;
2706
2707
2708
	XHR.prototype = {
2709
		status: 0,
2710
		statusText: '',
2711
		constructor: XHR,
2712
2713
		getResponseHeader: function (name){
2714
			return this.xhr.getResponseHeader(name);
2715
		},
2716
2717
		getAllResponseHeaders: function (){
2718
			return this.xhr.getAllResponseHeaders() || {};
2719
		},
2720
2721
		end: function (status, statusText){
2722
			var _this = this, options = _this.options;
2723
2724
			_this.end		=
2725
			_this.abort		= noop;
2726
			_this.status	= status;
2727
2728
			if( statusText ){
2729
				_this.statusText = statusText;
2730
			}
2731
2732
			api.log('xhr.end:', status, statusText);
2733
			options.complete(status == 200 || status == 201 ? false : _this.statusText || 'unknown', _this);
2734
2735
			if( _this.xhr && _this.xhr.node ){
2736
				setTimeout(function (){
2737
					var node = _this.xhr.node;
2738
					try { node.parentNode.removeChild(node); } catch (e){}
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...
2739
					try { delete window[_this.uid]; } catch (e){}
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...
2740
					window[_this.uid] = _this.xhr.node = null;
2741
				}, 9);
2742
			}
2743
		},
2744
2745
		abort: function (){
2746
			this.end(0, 'abort');
2747
2748
			if( this.xhr ){
2749
				this.xhr.aborted = true;
2750
				this.xhr.abort();
2751
			}
2752
		},
2753
2754
		send: function (FormData){
2755
			var _this = this, options = this.options;
2756
2757
			FormData.toData(function (data){
2758
				// Start uploading
2759
				options.upload(options, _this);
2760
				_this._send.call(_this, options, data);
2761
			}, options);
2762
		},
2763
2764
		_send: function (options, data){
2765
			var _this = this, xhr, uid = _this.uid, onloadFuncName = _this.uid + "Load", url = options.url;
2766
2767
			api.log('XHR._send:', data);
2768
2769
			if( !options.cache ){
2770
				// No cache
2771
				url += (~url.indexOf('?') ? '&' : '?') + api.uid();
2772
			}
2773
2774
			if( data.nodeName ){
2775
				var jsonp = options.jsonp;
2776
2777
				// prepare callback in GET
2778
				url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
2779
2780
				// legacy
2781
				options.upload(options, _this);
2782
2783
				var
2784
					onPostMessage = function (evt){
2785
						if( ~url.indexOf(evt.origin) ){
2786
							try {
2787
								var result = api.parseJSON(evt.data);
2788
								if( result.id == uid ){
2789
									complete(result.status, result.statusText, result.response);
2790
								}
2791
							} catch( err ){
2792
								complete(0, err.message);
2793
							}
2794
						}
2795
					},
2796
2797
					// jsonp-callack
2798
					complete = window[uid] = function (status, statusText, response){
2799
						_this.readyState	= 4;
2800
						_this.responseText	= response;
2801
						_this.end(status, statusText);
2802
2803
						api.event.off(window, 'message', onPostMessage);
2804
						window[uid] = xhr = transport = window[onloadFuncName] = null;
2805
					}
2806
				;
2807
2808
				_this.xhr.abort = function (){
2809
					try {
2810
						if( transport.stop ){ transport.stop(); }
2811
						else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
2812
						else { transport.contentWindow.document.execCommand('Stop'); }
2813
					}
2814
					catch (er) {}
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...
2815
					complete(0, "abort");
2816
				};
2817
2818
				api.event.on(window, 'message', onPostMessage);
2819
2820
				window[onloadFuncName] = function (){
2821
					try {
2822
						var
2823
							  win = transport.contentWindow
2824
							, doc = win.document
2825
							, result = win.result || api.parseJSON(doc.body.innerHTML)
2826
						;
2827
						complete(result.status, result.statusText, result.response);
2828
					} catch (e){
2829
						api.log('[transport.onload]', e);
2830
					}
2831
				};
2832
2833
				xhr = document.createElement('div');
2834
				xhr.innerHTML = '<form target="'+ uid +'" action="../../../ng-file-upload-12.2.12/src/'+ url +'" method="POST" enctype="multipart/form-data" style="position: absolute; top: -1000px; overflow: hidden; width: 1px; height: 1px;">'
2835
							+ '<iframe name="'+ uid +'" src="javascript:false;" onload="' + onloadFuncName + '()"></iframe>'
2836
							+ (jsonp && (options.url.indexOf('=?') < 0) ? '<input value="'+ uid +'" name="'+jsonp+'" type="hidden"/>' : '')
2837
							+ '</form>'
2838
				;
2839
2840
				// get form-data & transport
2841
				var
2842
					  form = xhr.getElementsByTagName('form')[0]
2843
					, transport = xhr.getElementsByTagName('iframe')[0]
2844
				;
2845
2846
				form.appendChild(data);
2847
2848
				api.log(form.parentNode.innerHTML);
2849
2850
				// append to DOM
2851
				document.body.appendChild(xhr);
2852
2853
				// keep a reference to node-transport
2854
				_this.xhr.node = xhr;
2855
2856
				// send
2857
				_this.readyState = 2; // loaded
2858
				form.submit();
2859
				form = null;
0 ignored issues
show
Unused Code introduced by
The assignment to form seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
2860
			}
2861
			else {
2862
				// Clean url
2863
				url = url.replace(/([a-z]+)=(\?)&?/i, '');
2864
2865
				// html5
2866
				if (this.xhr && this.xhr.aborted) {
2867
					api.log("Error: already aborted");
2868
					return;
2869
				}
2870
				xhr = _this.xhr = api.getXHR();
2871
2872
				if (data.params) {
2873
					url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
2874
				}
2875
2876
				xhr.open('POST', url, true);
2877
2878
				if( api.withCredentials ){
2879
					xhr.withCredentials = "true";
2880
				}
2881
2882
				if( !options.headers || !options.headers['X-Requested-With'] ){
2883
					xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2884
				}
2885
2886
				api.each(options.headers, function (val, key){
2887
					xhr.setRequestHeader(key, val);
2888
				});
2889
2890
2891
				if ( options._chunked ) {
2892
					// chunked upload
2893
					if( xhr.upload ){
2894
						xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
2895
							if (!data.retry) {
2896
								// show progress only for correct chunk uploads
2897
								options.progress({
2898
									  type:			evt.type
2899
									, total:		data.size
2900
									, loaded:		data.start + evt.loaded
2901
									, totalSize:	data.size
2902
								}, _this, options);
2903
							}
2904
						}, 100), false);
2905
					}
2906
2907
					xhr.onreadystatechange = function (){
2908
						var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10);
2909
2910
						_this.status     = xhr.status;
2911
						_this.statusText = xhr.statusText;
2912
						_this.readyState = xhr.readyState;
2913
2914
						if( xhr.readyState == 4 ){
2915
							try {
2916
								for( var k in _xhrResponsePostfix ){
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
2917
									_this['response'+k]  = xhr['response'+k];
2918
								}
2919
							}catch(_){}
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...
2920
							xhr.onreadystatechange = null;
2921
2922
							if (!xhr.status || xhr.status - 201 > 0) {
2923
								api.log("Error: " + xhr.status);
2924
								// some kind of error
2925
								// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
2926
								// up - server error
2927
								if (((!xhr.status && !xhr.aborted) || 500 == xhr.status || 416 == xhr.status) && ++data.retry <= options.chunkUploadRetry) {
2928
									// let's try again the same chunk
2929
									// only applicable for recoverable error codes 500 && 416
2930
									var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
2931
2932
									// inform about recoverable problems
2933
									options.pause(data.file, options);
2934
2935
									// smart restart if server reports about the last known byte
2936
									api.log("X-Last-Known-Byte: " + lkb);
2937
									if (lkb) {
2938
										data.end = lkb;
2939
									} else {
2940
										data.end = data.start - 1;
2941
										if (416 == xhr.status) {
2942
											data.end = data.end - options.chunkSize;
2943
										}
2944
									}
2945
2946
									setTimeout(function () {
2947
										_this._send(options, data);
2948
									}, delay);
2949
								} else {
2950
									// no mo retries
2951
									_this.end(xhr.status);
2952
								}
2953
							} else {
2954
								// success
2955
								data.retry = 0;
2956
2957
								if (data.end == data.size - 1) {
2958
									// finished
2959
									_this.end(xhr.status);
2960
								} else {
2961
									// next chunk
2962
2963
									// shift position if server reports about the last known byte
2964
									api.log("X-Last-Known-Byte: " + lkb);
2965
									if (lkb) {
2966
										data.end = lkb;
2967
									}
2968
									data.file.FileAPIReadPosition = data.end;
2969
2970
									setTimeout(function () {
2971
										_this._send(options, data);
2972
									}, 0);
2973
								}
2974
							}
2975
2976
							xhr = null;
2977
						}
2978
					};
2979
2980
					data.start = data.end + 1;
2981
					data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
2982
2983
					// Retrieve a slice of file
2984
					var
2985
						  file = data.file
2986
						, slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
2987
					;
2988
2989
					if( data.size && !slice.size ){
2990
						setTimeout(function (){
2991
							_this.end(-1);
2992
						});
2993
					} else {
2994
						xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
2995
						xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
2996
						xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
2997
2998
						xhr.send(slice);
2999
					}
3000
3001
					file = slice = null;
0 ignored issues
show
Unused Code introduced by
The assignment to slice seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
3002
				} else {
3003
					// single piece upload
3004
					if( xhr.upload ){
3005
						// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
3006
						xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
3007
							options.progress(evt, _this, options);
3008
						}, 100), false);
3009
					}
3010
3011
					xhr.onreadystatechange = function (){
3012
						_this.status     = xhr.status;
3013
						_this.statusText = xhr.statusText;
3014
						_this.readyState = xhr.readyState;
3015
3016
						if( xhr.readyState == 4 ){
3017
							for( var k in _xhrResponsePostfix ){
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
3018
								_this['response'+k]  = xhr['response'+k];
3019
							}
3020
							xhr.onreadystatechange = null;
3021
3022
							if (!xhr.status || xhr.status > 201) {
3023
								api.log("Error: " + xhr.status);
3024
								if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
3025
									options.retry = (options.retry || 0) + 1;
3026
									var delay = api.networkDownRetryTimeout;
3027
3028
									// inform about recoverable problems
3029
									options.pause(options.file, options);
3030
3031
									setTimeout(function () {
3032
										_this._send(options, data);
3033
									}, delay);
3034
								} else {
3035
									//success
3036
									_this.end(xhr.status);
3037
								}
3038
							} else {
3039
								//success
3040
								_this.end(xhr.status);
3041
							}
3042
3043
							xhr = null;
3044
						}
3045
					};
3046
3047
					if( api.isArray(data) ){
3048
						// multipart
3049
						xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
3050
						var rawData = data.join('') +'--_'+ api.expando +'--';
3051
3052
						/** @namespace  xhr.sendAsBinary  https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
3053
						if( xhr.sendAsBinary ){
3054
							xhr.sendAsBinary(rawData);
3055
						}
3056
						else {
3057
							var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
3058
							xhr.send(new Uint8Array(bytes).buffer);
3059
3060
						}
3061
					} else {
3062
						// FormData
3063
						xhr.send(data);
3064
					}
3065
				}
3066
			}
3067
		}
3068
	};
3069
3070
3071
	// @export
3072
	api.XHR = XHR;
3073
})(window, FileAPI);
3074
3075
/**
3076
 * @class	FileAPI.Camera
3077
 * @author	RubaXa	<[email protected]>
3078
 * @support	Chrome 21+, FF 18+, Opera 12+
3079
 */
3080
3081
/*global window, FileAPI, jQuery */
3082
/** @namespace LocalMediaStream -- https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API#LocalMediaStream */
3083
(function (window, api){
3084
	"use strict";
3085
3086
	var
3087
		URL = window.URL || window.webkitURL,
3088
3089
		document = window.document,
3090
		navigator = window.navigator,
3091
3092
		getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia,
3093
3094
		html5 = !!getMedia
3095
	;
3096
3097
3098
	// Support "media"
3099
	api.support.media = html5;
3100
3101
3102
	var Camera = function (video){
3103
		this.video = video;
3104
	};
3105
3106
3107
	Camera.prototype = {
3108
		isActive: function (){
3109
			return	!!this._active;
3110
		},
3111
3112
3113
		/**
3114
		 * Start camera streaming
3115
		 * @param	{Function}	callback
3116
		 */
3117
		start: function (callback){
3118
			var
3119
				  _this = this
3120
				, video = _this.video
3121
				, _successId
3122
				, _failId
3123
				, _complete = function (err){
3124
					_this._active = !err;
3125
					clearTimeout(_failId);
3126
					clearTimeout(_successId);
3127
//					api.event.off(video, 'loadedmetadata', _complete);
3128
					callback && callback(err, _this);
3129
				}
3130
			;
3131
3132
			getMedia.call(navigator, { video: true }, function (stream/**LocalMediaStream*/){
3133
				// Success
3134
				_this.stream = stream;
3135
3136
//				api.event.on(video, 'loadedmetadata', function (){
3137
//					_complete(null);
3138
//				});
3139
3140
				// Set camera stream
3141
				video.src = URL.createObjectURL(stream);
3142
3143
				// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
3144
				// See crbug.com/110938.
3145
				_successId = setInterval(function (){
3146
					if( _detectVideoSignal(video) ){
3147
						_complete(null);
3148
					}
3149
				}, 1000);
3150
3151
				_failId = setTimeout(function (){
3152
					_complete('timeout');
3153
				}, 5000);
3154
3155
				// Go-go-go!
3156
				video.play();
3157
			}, _complete/*error*/);
3158
		},
3159
3160
3161
		/**
3162
		 * Stop camera streaming
3163
		 */
3164
		stop: function (){
3165
			try {
3166
				this._active = false;
3167
				this.video.pause();
3168
				this.stream.stop();
3169
			} catch( err ){ }
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...
3170
		},
3171
3172
3173
		/**
3174
		 * Create screenshot
3175
		 * @return {FileAPI.Camera.Shot}
3176
		 */
3177
		shot: function (){
3178
			return	new Shot(this.video);
3179
		}
3180
	};
3181
3182
3183
	/**
3184
	 * Get camera element from container
3185
	 *
3186
	 * @static
3187
	 * @param	{HTMLElement}	el
3188
	 * @return	{Camera}
3189
	 */
3190
	Camera.get = function (el){
3191
		return	new Camera(el.firstChild);
3192
	};
3193
3194
3195
	/**
3196
	 * Publish camera element into container
3197
	 *
3198
	 * @static
3199
	 * @param	{HTMLElement}	el
3200
	 * @param	{Object}		options
3201
	 * @param	{Function}		[callback]
3202
	 */
3203
	Camera.publish = function (el, options, callback){
3204
		if( typeof options == 'function' ){
3205
			callback = options;
3206
			options = {};
3207
		}
3208
3209
		// Dimensions of "camera"
3210
		options = api.extend({}, {
3211
			  width:	'100%'
3212
			, height:	'100%'
3213
			, start:	true
3214
		}, options);
3215
3216
3217
		if( el.jquery ){
3218
			// Extract first element, from jQuery collection
3219
			el = el[0];
3220
		}
3221
3222
3223
		var doneFn = function (err){
3224
			if( err ){
3225
				callback(err);
3226
			}
3227
			else {
3228
				// Get camera
3229
				var cam = Camera.get(el);
3230
				if( options.start ){
3231
					cam.start(callback);
3232
				}
3233
				else {
3234
					callback(null, cam);
3235
				}
3236
			}
3237
		};
3238
3239
3240
		el.style.width	= _px(options.width);
3241
		el.style.height	= _px(options.height);
3242
3243
3244
		if( api.html5 && html5 ){
3245
			// Create video element
3246
			var video = document.createElement('video');
3247
3248
			// Set dimensions
3249
			video.style.width	= _px(options.width);
3250
			video.style.height	= _px(options.height);
3251
3252
			// Clean container
3253
			if( window.jQuery ){
3254
				jQuery(el).empty();
3255
			} else {
3256
				el.innerHTML = '';
3257
			}
3258
3259
			// Add "camera" to container
3260
			el.appendChild(video);
3261
3262
			// end
3263
			doneFn();
3264
		}
3265
		else {
3266
			Camera.fallback(el, options, doneFn);
3267
		}
3268
	};
3269
3270
3271
	Camera.fallback = function (el, options, callback){
3272
		callback('not_support_camera');
3273
	};
3274
3275
3276
	/**
3277
	 * @class	FileAPI.Camera.Shot
3278
	 */
3279
	var Shot = function (video){
3280
		var canvas	= video.nodeName ? api.Image.toCanvas(video) : video;
3281
		var shot	= api.Image(canvas);
3282
		shot.type	= 'image/png';
3283
		shot.width	= canvas.width;
3284
		shot.height	= canvas.height;
3285
		shot.size	= canvas.width * canvas.height * 4;
3286
		return	shot;
3287
	};
3288
3289
3290
	/**
3291
	 * Add "px" postfix, if value is a number
3292
	 *
3293
	 * @private
3294
	 * @param	{*}  val
3295
	 * @return	{String}
3296
	 */
3297
	function _px(val){
3298
		return	val >= 0 ? val + 'px' : val;
3299
	}
3300
3301
3302
	/**
3303
	 * @private
3304
	 * @param	{HTMLVideoElement} video
3305
	 * @return	{Boolean}
3306
	 */
3307
	function _detectVideoSignal(video){
3308
		var canvas = document.createElement('canvas'), ctx, res = false;
3309
		try {
3310
			ctx = canvas.getContext('2d');
3311
			ctx.drawImage(video, 0, 0, 1, 1);
3312
			res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
3313
		}
3314
		catch( e ){}
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...
3315
		return	res;
3316
	}
3317
3318
3319
	// @export
3320
	Camera.Shot	= Shot;
3321
	api.Camera	= Camera;
3322
})(window, FileAPI);
3323
3324
/**
3325
 * FileAPI fallback to Flash
3326
 *
3327
 * @flash-developer  "Vladimir Demidov" <[email protected]>
3328
 */
3329
3330
/*global window, ActiveXObject, FileAPI */
3331
(function (window, jQuery, api) {
3332
	"use strict";
3333
3334
	var
3335
		  document = window.document
3336
		, location = window.location
3337
		, navigator = window.navigator
3338
		, _each = api.each
3339
	;
3340
3341
3342
	api.support.flash = (function (){
3343
		var mime = navigator.mimeTypes, has = false;
3344
3345
		if( navigator.plugins && typeof navigator.plugins['Shockwave Flash'] == 'object' ){
3346
			has	= navigator.plugins['Shockwave Flash'].description && !(mime && mime['application/x-shockwave-flash'] && !mime['application/x-shockwave-flash'].enabledPlugin);
3347
		}
3348
		else {
3349
			try {
3350
				has	= !!(window.ActiveXObject && new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
3351
			}
3352
			catch(er){
3353
				api.log('Flash -- does not supported.');
3354
			}
3355
		}
3356
3357
		if( has && /^file:/i.test(location) ){
3358
			api.log('[warn] Flash does not work on `file:` protocol.');
3359
		}
3360
3361
		return	has;
3362
	})();
3363
3364
3365
	   api.support.flash
3366
	&& (0
3367
		|| !api.html5 || !api.support.html5
3368
		|| (api.cors && !api.support.cors)
3369
		|| (api.media && !api.support.media)
3370
	)
3371
	&& (function (){
3372
		var
3373
			  _attr  = api.uid()
3374
			, _retry = 0
3375
			, _files = {}
3376
			, _rhttp = /^https?:/i
3377
3378
			, flash = {
3379
				_fn: {},
3380
3381
3382
				/**
3383
				 * Publish flash-object
3384
				 *
3385
				 * @param {HTMLElement} el
3386
				 * @param {String} id
3387
				 * @param {Object} [opts]
3388
				 */
3389
				publish: function (el, id, opts){
3390
					opts = opts || {};
3391
					el.innerHTML = _makeFlashHTML({
0 ignored issues
show
Bug introduced by
The call to _makeFlashHTML seems to have too many arguments starting with opts.
Loading history...
3392
						id: id
3393
						, src: _getUrl(api.flashUrl, 'r=' + api.version)
3394
//						, src: _getUrl('http://v.demidov.boom.corp.mail.ru/uploaderfileapi/FlashFileAPI.swf?1')
3395
						, wmode: opts.camera ? '' : 'transparent'
3396
						, flashvars: 'callback=' + (opts.onEvent || 'FileAPI.Flash.onEvent')
3397
						+ '&flashId='+ id
3398
						+ '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
3399
						+ (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
3400
						+ '&timeout='+api.flashAbortTimeout
3401
						+ (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
3402
						+ '&debug='+(api.debug?"1":"")
3403
					}, opts);
3404
				},
3405
3406
3407
				/**
3408
				 * Initialization & preload flash object
3409
				 */
3410
				init: function (){
3411
					var child = document.body && document.body.firstChild;
3412
3413
					if( child ){
3414
						do {
3415
							if( child.nodeType == 1 ){
3416
								api.log('FlashAPI.state: awaiting');
3417
3418
								var dummy = document.createElement('div');
3419
3420
								dummy.id = '_' + _attr;
3421
3422
								_css(dummy, {
3423
									  top: 1
3424
									, right: 1
3425
									, width: 5
3426
									, height: 5
3427
									, position: 'absolute'
3428
									, zIndex: 1e6+'' // set max zIndex
3429
								});
3430
3431
								child.parentNode.insertBefore(dummy, child);
3432
								flash.publish(dummy, _attr);
3433
3434
								return;
3435
							}
3436
						}
3437
						while( child = child.nextSibling );
3438
					}
3439
3440
					if( _retry < 10 ){
3441
						setTimeout(flash.init, ++_retry*50);
3442
					}
3443
				},
3444
3445
3446
				ready: function (){
3447
					api.log('FlashAPI.state: ready');
3448
3449
					flash.ready = api.F;
3450
					flash.isReady = true;
3451
					flash.patch();
3452
					flash.patchCamera && flash.patchCamera();
3453
					api.event.on(document, 'mouseover', flash.mouseover);
3454
					api.event.on(document, 'click', function (evt){
3455
						if( flash.mouseover(evt) ){
3456
							evt.preventDefault
3457
								? evt.preventDefault()
3458
								: (evt.returnValue = true)
3459
							;
3460
						}
3461
					});
3462
				},
3463
3464
3465
				getEl: function (){
3466
					return	document.getElementById('_'+_attr);
3467
				},
3468
3469
3470
				getWrapper: function (node){
3471
					do {
3472
						if( /js-fileapi-wrapper/.test(node.className) ){
3473
							return	node;
3474
						}
3475
					}
3476
					while( (node = node.parentNode) && (node !== document.body) );
3477
				},
3478
				
3479
				disableMouseover: false,
3480
3481
				mouseover: function (evt){
3482
					if (!flash.disableMouseover) {
3483
						var target = api.event.fix(evt).target;
3484
	
3485
						if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
3486
							var
3487
								  state = target.getAttribute(_attr)
3488
								, wrapper = flash.getWrapper(target)
3489
							;
3490
	
3491
							if( api.multiFlash ){
3492
								// check state:
3493
								//   i — published
3494
								//   i — initialization
3495
								//   r — ready
3496
								if( state == 'i' || state == 'r' ){
3497
									// publish fail
3498
									return	false;
3499
								}
3500
								else if( state != 'p' ){
3501
									// set "init" state
3502
									target.setAttribute(_attr, 'i');
3503
	
3504
									var dummy = document.createElement('div');
3505
	
3506
									if( !wrapper ){
3507
										api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found');
3508
										return;
3509
									}
3510
	
3511
									_css(dummy, {
3512
										  top:    0
3513
										, left:   0
3514
										, width:  target.offsetWidth
3515
										, height: target.offsetHeight
3516
										, zIndex: 1e6+'' // set max zIndex
3517
										, position: 'absolute'
3518
									});
3519
	
3520
									wrapper.appendChild(dummy);
3521
									flash.publish(dummy, api.uid());
3522
	
3523
									// set "publish" state
3524
									target.setAttribute(_attr, 'p');
3525
								}
3526
	
3527
								return	true;
3528
							}
3529
							else if( wrapper ){
3530
								// Use one flash element
3531
								var box = _getDimensions(wrapper);
3532
								_css(flash.getEl(), box);
3533
	
3534
								// Set current input
3535
								flash.curInp = target;
3536
							}
3537
						}
3538
						else if( !/object|embed/i.test(target.nodeName) ){
3539
							_css(flash.getEl(), { top: 1, left: 1, width: 5, height: 5 });
3540
						}
3541
					}
3542
				},
3543
3544
				onEvent: function (evt){
3545
					var type = evt.type;
3546
					
3547
					if( type == 'ready' ){
3548
						try {
3549
							// set "ready" state
3550
							flash.getInput(evt.flashId).setAttribute(_attr, 'r');
3551
						} catch (e){
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...
3552
						}
3553
3554
						flash.ready();
3555
						setTimeout(function (){ flash.mouseenter(evt); }, 50);
3556
						return	true;
3557
					}
3558
					else if( type === 'ping' ){
3559
						api.log('(flash -> js).ping:', [evt.status, evt.savedStatus], evt.error);
3560
					}
3561
					else if( type === 'log' ){
3562
						api.log('(flash -> js).log:', evt.target);
3563
					}
3564
					else if( type in flash ){
3565
						setTimeout(function (){
3566
							api.log('FlashAPI.event.'+evt.type+':', evt);
3567
							flash[type](evt);
3568
						}, 1);
3569
					}
3570
				},
3571
				mouseDown: function(evt) {
3572
					flash.disableMouseover = true;
3573
				},
3574
				cancel: function(evt) {
3575
					flash.disableMouseover = false;
3576
				},
3577
				mouseenter: function (evt){
3578
					var node = flash.getInput(evt.flashId);
3579
3580
					if( node ){
3581
						// Set multiple mode
3582
						flash.cmd(evt, 'multiple', node.getAttribute('multiple') != null);
3583
3584
3585
						// Set files filter
3586
						var accept = [], exts = {};
3587
3588
						_each((node.getAttribute('accept') || '').split(/,\s*/), function (mime){
3589
							api.accept[mime] && _each(api.accept[mime].split(' '), function (ext){
3590
								exts[ext] = 1;
3591
							});
3592
						});
3593
3594
						_each(exts, function (i, ext){
3595
							accept.push( ext );
3596
						});
3597
3598
						flash.cmd(evt, 'accept', accept.length ? accept.join(',')+','+accept.join(',').toUpperCase() : '*');
3599
					}
3600
				},
3601
3602
3603
				get: function (id){
3604
					return	document[id] || window[id] || document.embeds[id];
3605
				},
3606
3607
3608
				getInput: function (id){
3609
					if( api.multiFlash ){
3610
						try {
3611
							var node = flash.getWrapper(flash.get(id));
3612
							if( node ){
3613
								return node.getElementsByTagName('input')[0];
3614
							}
3615
						} catch (e){
3616
							api.log('[err] Can not find "input" by flashId:', id, e);
3617
						}
3618
					} else {
3619
						return	flash.curInp;
3620
					}
3621
				},
3622
3623
3624
				select: function (evt){
3625
					try {
3626
						var
3627
							  inp = flash.getInput(evt.flashId)
3628
							, uid = api.uid(inp)
3629
							, files = evt.target.files
3630
							, event
3631
						;
3632
						_each(files, function (file){
3633
							api.checkFileObj(file);
3634
						});
3635
	
3636
						_files[uid] = files;
3637
	
3638
						if( document.createEvent ){
3639
							event = document.createEvent('Event');
3640
							event.files = files;
3641
							event.initEvent('change', true, true);
3642
							inp.dispatchEvent(event);
3643
						}
3644
						else if( jQuery ){
3645
							jQuery(inp).trigger({ type: 'change', files: files });
3646
						}
3647
						else {
3648
							event = document.createEventObject();
3649
							event.files = files;
3650
							inp.fireEvent('onchange', event);
3651
						}
3652
					} finally {
3653
						flash.disableMouseover = false;
3654
					}
3655
				},
3656
3657
				interval: null,
3658
				cmd: function (id, name, data, last) {
3659
					if (flash.uploadInProgress && flash.readInProgress) {
3660
						setTimeout(function() {
3661
							flash.cmd(id, name, data, last);
3662
						}, 100);
3663
					} else {
3664
						this.cmdFn(id, name, data, last);
3665
					}
3666
				},
3667
				
3668
				cmdFn: function(id, name, data, last) {
3669
					try {
3670
						api.log('(js -> flash).'+name+':', data);
3671
						return flash.get(id.flashId || id).cmd(name, data);
3672
					} catch (e){
3673
						api.log('(js -> flash).onError:', e);
3674
						if( !last ){
3675
							// try again
3676
							setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
3677
						}
3678
					}
3679
				},
3680
3681
				patch: function (){
3682
					api.flashEngine = true;
3683
3684
					// FileAPI
3685
					_inherit(api, {
3686
						readAsDataURL: function (file, callback){
3687
							if( _isHtmlFile(file) ){
3688
								this.parent.apply(this, arguments);
3689
							}
3690
							else {
3691
								api.log('FlashAPI.readAsBase64');
3692
								flash.readInProgress = true;
3693
								flash.cmd(file, 'readAsBase64', {
3694
									id: file.id,
3695
									callback: _wrap(function _(err, base64){
3696
										flash.readInProgress = false;
3697
										_unwrap(_);
3698
3699
										api.log('FlashAPI.readAsBase64:', err);
3700
3701
										callback({
3702
											  type: err ? 'error' : 'load'
3703
											, error: err
3704
											, result: 'data:'+ file.type +';base64,'+ base64
3705
										});
3706
									})
3707
								});
3708
							}
3709
						},
3710
3711
						readAsText: function (file, encoding, callback){
3712
							if( callback ){
3713
								api.log('[warn] FlashAPI.readAsText not supported `encoding` param');
3714
							} else {
3715
								callback = encoding;
3716
							}
3717
3718
							api.readAsDataURL(file, function (evt){
3719
								if( evt.type == 'load' ){
3720
									try {
3721
										evt.result = window.atob(evt.result.split(';base64,')[1]);
3722
									} catch( err ){
3723
										evt.type = 'error';
3724
										evt.error = err.toString();
3725
									}
3726
								}
3727
								callback(evt);
3728
							});
3729
						},
3730
						
3731
						getFiles: function (input, filter, callback){
3732
							if( callback ){
3733
								api.filterFiles(api.getFiles(input), filter, callback);
3734
								return null;
3735
							}
3736
3737
							var files = api.isArray(input) ? input : _files[api.uid(input.target || input.srcElement || input)];
3738
3739
3740
							if( !files ){
3741
								// Файлов нету, вызываем родительский метод
3742
								return	this.parent.apply(this, arguments);
3743
							}
3744
3745
3746
							if( filter ){
3747
								filter	= api.getFilesFilter(filter);
3748
								files	= api.filter(files, function (file){ return filter.test(file.name); });
3749
							}
3750
3751
							return	files;
3752
						},
3753
3754
3755
						getInfo: function (file, fn){
3756
							if( _isHtmlFile(file) ){
3757
								this.parent.apply(this, arguments);
3758
							}
3759
							else if( file.isShot ){
3760
								fn(null, file.info = {
3761
									width: file.width,
3762
									height: file.height
3763
								});
3764
							}
3765
							else {
3766
								if( !file.__info ){
3767
									var defer = file.__info = api.defer();
3768
3769
//									flash.cmd(file, 'getFileInfo', {
3770
//										  id: file.id
3771
//										, callback: _wrap(function _(err, info){
3772
//											_unwrap(_);
3773
//											defer.resolve(err, file.info = info);
3774
//										})
3775
//									});
3776
									defer.resolve(null, file.info = null);
3777
3778
								}
3779
3780
								file.__info.then(fn);
3781
							}
3782
						}
3783
					});
3784
3785
3786
					// FileAPI.Image
3787
					api.support.transform = true;
3788
					api.Image && _inherit(api.Image.prototype, {
3789
						get: function (fn, scaleMode){
3790
							this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
3791
							return this.parent(fn);
3792
						},
3793
3794
						_load: function (file, fn){
3795
							api.log('FlashAPI.Image._load:', file);
3796
3797
							if( _isHtmlFile(file) ){
3798
								this.parent.apply(this, arguments);
3799
							}
3800
							else {
3801
								var _this = this;
3802
								api.getInfo(file, function (err){
3803
									fn.call(_this, err, file);
3804
								});
3805
							}
3806
						},
3807
3808
						_apply: function (file, fn){
3809
							api.log('FlashAPI.Image._apply:', file);
3810
3811
							if( _isHtmlFile(file) ){
3812
								this.parent.apply(this, arguments);
3813
							}
3814
							else {
3815
								var m = this.getMatrix(file.info), doneFn = fn;
3816
3817
								flash.cmd(file, 'imageTransform', {
3818
									  id: file.id
3819
									, matrix: m
3820
									, callback: _wrap(function _(err, base64){
3821
										api.log('FlashAPI.Image._apply.callback:', err);
3822
										_unwrap(_);
3823
3824
										if( err ){
3825
											doneFn(err);
3826
										}
3827
										else if( !api.support.html5 && (!api.support.dataURI || base64.length > 3e4) ){
3828
											_makeFlashImage({
3829
												  width:	(m.deg % 180) ? m.dh : m.dw
3830
												, height:	(m.deg % 180) ? m.dw : m.dh
3831
												, scale:	m.scaleMode
3832
											}, base64, doneFn);
3833
										}
3834
										else {
3835
											if( m.filter ){
3836
												doneFn = function (err, img){
3837
													if( err ){
3838
														fn(err);
3839
													}
3840
													else {
3841
														api.Image.applyFilter(img, m.filter, function (){
3842
															fn(err, this.canvas);
3843
														});
3844
													}
3845
												};
3846
											}
3847
3848
											api.newImage('data:'+ file.type +';base64,'+ base64, doneFn);
3849
										}
3850
									})
3851
								});
3852
							}
3853
						},
3854
3855
						toData: function (fn){
3856
							var
3857
								  file = this.file
3858
								, info = file.info
3859
								, matrix = this.getMatrix(info)
3860
							;
3861
							api.log('FlashAPI.Image.toData');
3862
3863
							if( _isHtmlFile(file) ){
3864
								this.parent.apply(this, arguments);
3865
							}
3866
							else {
3867
								if( matrix.deg == 'auto' ){
3868
									matrix.deg = api.Image.exifOrientation[info && info.exif && info.exif.Orientation] || 0;
3869
								}
3870
3871
								fn.call(this, !file.info, {
3872
									  id:		file.id
3873
									, flashId:	file.flashId
3874
									, name:		file.name
3875
									, type:		file.type
3876
									, matrix:	matrix
3877
								});
3878
							}
3879
						}
3880
					});
3881
3882
3883
					api.Image && _inherit(api.Image, {
3884
						fromDataURL: function (dataURL, size, callback){
3885
							if( !api.support.dataURI || dataURL.length > 3e4 ){
3886
								_makeFlashImage(
3887
									  api.extend({ scale: 'exactFit' }, size)
3888
									, dataURL.replace(/^data:[^,]+,/, '')
3889
									, function (err, el){ callback(el); }
3890
								);
3891
							}
3892
							else {
3893
								this.parent(dataURL, size, callback);
3894
							}
3895
						}
3896
					});
3897
3898
					// FileAPI.Form
3899
					_inherit(api.Form.prototype, {
3900
						toData: function (fn){
3901
							var items = this.items, i = items.length;
3902
3903
							for( ; i--; ){
3904
								if( items[i].file && _isHtmlFile(items[i].blob) ){
3905
									return this.parent.apply(this, arguments);
3906
								}
3907
							}
3908
3909
							api.log('FlashAPI.Form.toData');
3910
							fn(items);
3911
						}
3912
					});
3913
3914
3915
					// FileAPI.XHR
3916
					_inherit(api.XHR.prototype, {
3917
						_send: function (options, formData){
3918
							if(
3919
								   formData.nodeName
3920
								|| formData.append && api.support.html5
3921
								|| api.isArray(formData) && (typeof formData[0] === 'string')
3922
							){
3923
								// HTML5, Multipart or IFrame
3924
								return	this.parent.apply(this, arguments);
3925
							}
3926
3927
3928
							var
3929
								  data = {}
3930
								, files = {}
3931
								, _this = this
3932
								, flashId
3933
								, fileId
3934
							;
3935
3936
							_each(formData, function (item){
3937
								if( item.file ){
3938
									files[item.name] = item = _getFileDescr(item.blob);
3939
									fileId  = item.id;
3940
									flashId = item.flashId;
3941
								}
3942
								else {
3943
									data[item.name] = item.blob;
3944
								}
3945
							});
3946
3947
							if( !fileId ){
3948
								flashId = _attr;
3949
							}
3950
3951
							if( !flashId ){
3952
								api.log('[err] FlashAPI._send: flashId -- undefined');
3953
								return this.parent.apply(this, arguments);
3954
							}
3955
							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...
3956
								api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
3957
							}
3958
3959
							_this.xhr = {
3960
								headers: {},
3961
								abort: function (){ flash.uploadInProgress = false; flash.cmd(flashId, 'abort', { id: fileId }); },
3962
								getResponseHeader: function (name){ return this.headers[name]; },
3963
								getAllResponseHeaders: function (){ return this.headers; }
3964
							};
3965
3966
							var queue = api.queue(function (){
3967
								flash.uploadInProgress = true;
3968
								flash.cmd(flashId, 'upload', {
3969
									  url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
3970
									, data: data
3971
									, files: fileId ? files : null
3972
									, headers: options.headers || {}
3973
									, callback: _wrap(function upload(evt){
3974
										var type = evt.type, result = evt.result;
3975
3976
										api.log('FlashAPI.upload.'+type);
3977
3978
										if( type == 'progress' ){
3979
											evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
3980
											evt.lengthComputable = true;
3981
											options.progress(evt);
3982
										}
3983
										else if( type == 'complete' ){
3984
											flash.uploadInProgress = false;
3985
											_unwrap(upload);
3986
3987
											if( typeof result == 'string' ){
3988
												_this.responseText	= result.replace(/%22/g, "\"").replace(/%5c/g, "\\").replace(/%26/g, "&").replace(/%25/g, "%");
3989
											}
3990
3991
											_this.end(evt.status || 200);
3992
										}
3993
										else if( type == 'abort' || type == 'error' ){
3994
											flash.uploadInProgress = false;
3995
											_this.end(evt.status || 0, evt.message);
3996
											_unwrap(upload);
3997
										}
3998
									})
3999
								});
4000
							});
4001
4002
4003
							// #2174: FileReference.load() call while FileReference.upload() or vice versa
4004
							_each(files, function (file){
4005
								queue.inc();
4006
								api.getInfo(file, queue.next);
4007
							});
4008
4009
							queue.check();
4010
						}
4011
					});
4012
				}
4013
			}
4014
		;
4015
4016
4017
		function _makeFlashHTML(opts){
4018
			return ('<object id="#id#" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'">'
4019
				+ '<param name="movie" value="#src#" />'
4020
				+ '<param name="flashvars" value="#flashvars#" />'
4021
				+ '<param name="swliveconnect" value="true" />'
4022
				+ '<param name="allowscriptaccess" value="always" />'
4023
				+ '<param name="allownetworking" value="all" />'
4024
				+ '<param name="menu" value="false" />'
4025
				+ '<param name="wmode" value="#wmode#" />'
4026
				+ '<embed flashvars="#flashvars#" swliveconnect="true" allownetworking="all" allowscriptaccess="always" name="#id#" src="#src#" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'" menu="false" wmode="transparent" type="application/x-shockwave-flash"></embed>'
4027
				+ '</object>').replace(/#(\w+)#/ig, function (a, name){ return opts[name]; })
4028
			;
4029
		}
4030
4031
4032
		function _css(el, css){
4033
			if( el && el.style ){
4034
				var key, val;
4035
				for( key in css ){
4036
					val = css[key];
4037
					if( typeof val == 'number' ){
4038
						val += 'px';
4039
					}
4040
					try { el.style[key] = val; } catch (e) {}
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...
4041
				}
4042
				
4043
			}
4044
		}
4045
4046
4047
		function _inherit(obj, methods){
4048
			_each(methods, function (fn, name){
4049
				var prev = obj[name];
4050
				obj[name] = function (){
4051
					this.parent = prev;
4052
					return fn.apply(this, arguments);
4053
				};
4054
			});
4055
		}
4056
4057
		function _isHtmlFile(file){
4058
			return	file && !file.flashId;
4059
		}
4060
4061
		function _wrap(fn){
4062
			var id = fn.wid = api.uid();
4063
			flash._fn[id] = fn;
4064
			return	'FileAPI.Flash._fn.'+id;
4065
		}
4066
4067
4068
		function _unwrap(fn){
4069
			try {
4070
				flash._fn[fn.wid] = null;
4071
				delete	flash._fn[fn.wid];
4072
			}
4073
			catch(e){}
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...
4074
		}
4075
4076
4077
		function _getUrl(url, params){
4078
			if( !_rhttp.test(url) ){
4079
				if( /^\.\//.test(url) || '/' != url.charAt(0) ){
4080
					var path = location.pathname;
4081
					path = path.substr(0, path.lastIndexOf('/'));
4082
					url = (path +'/'+ url).replace('/./', '/');
4083
				}
4084
4085
				if( '//' != url.substr(0, 2) ){
4086
					url = '//' + location.host + url;
4087
				}
4088
4089
				if( !_rhttp.test(url) ){
4090
					url = location.protocol + url;
4091
				}
4092
			}
4093
4094
			if( params ){
4095
				url += (/\?/.test(url) ? '&' : '?') + params;
4096
			}
4097
4098
			return	url;
4099
		}
4100
4101
4102
		function _makeFlashImage(opts, base64, fn){
4103
			var
4104
				  key
4105
				, flashId = api.uid()
4106
				, el = document.createElement('div')
4107
				, attempts = 10
4108
			;
4109
4110
			for( key in opts ){
4111
				el.setAttribute(key, opts[key]);
4112
				el[key] = opts[key];
4113
			}
4114
4115
			_css(el, opts);
4116
4117
			opts.width	= '100%';
4118
			opts.height	= '100%';
4119
4120
			el.innerHTML = _makeFlashHTML(api.extend({
4121
				  id: flashId
4122
				, src: _getUrl(api.flashImageUrl, 'r='+ api.uid())
4123
				, wmode: 'opaque'
4124
				, flashvars: 'scale='+ opts.scale +'&callback='+_wrap(function _(){
4125
					_unwrap(_);
4126
					if( --attempts > 0 ){
4127
						_setImage();
4128
					}
4129
					return true;
4130
				})
4131
			}, opts));
4132
4133
			function _setImage(){
4134
				try {
4135
					// Get flash-object by id
4136
					var img = flash.get(flashId);
4137
					img.setImage(base64);
4138
				} catch (e){
4139
					api.log('[err] FlashAPI.Preview.setImage -- can not set "base64":', e);
4140
				}
4141
			}
4142
4143
			fn(false, el);
4144
			el = null;
0 ignored issues
show
Unused Code introduced by
The assignment to el seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
4145
		}
4146
4147
4148
		function _getFileDescr(file){
4149
			return	{
4150
				  id: file.id
4151
				, name: file.name
4152
				, matrix: file.matrix
4153
				, flashId: file.flashId
4154
			};
4155
		}
4156
4157
4158
		function _getDimensions(el){
4159
			var
4160
				  box = el.getBoundingClientRect()
0 ignored issues
show
Unused Code introduced by
The variable box seems to be never used. Consider removing it.
Loading history...
4161
				, body = document.body
4162
				, docEl = (el && el.ownerDocument).documentElement
0 ignored issues
show
Unused Code introduced by
The variable docEl seems to be never used. Consider removing it.
Loading history...
4163
			;
4164
			
4165
			function getOffset(obj) {
4166
			    var left, top;
4167
			    left = top = 0;
4168
			    if (obj.offsetParent) {
4169
			        do {
4170
			            left += obj.offsetLeft;
4171
			            top  += obj.offsetTop;
4172
			        } while (obj = obj.offsetParent);
4173
			    }
4174
			    return {
4175
			        left : left,
4176
			        top : top
4177
			    };
4178
			};
4179
			
4180
			return {
4181
				  top:		getOffset(el).top
4182
				, left:		getOffset(el).left
4183
				, width:	el.offsetWidth
4184
				, height:	el.offsetHeight
4185
			};
4186
		}
4187
4188
		// @export
4189
		api.Flash = flash;
4190
4191
4192
		// Check dataURI support
4193
		api.newImage('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', function (err, img){
4194
			api.support.dataURI = !(img.width != 1 || img.height != 1);
4195
			flash.init();
4196
		});
4197
	})();
4198
})(window, window.jQuery, FileAPI);
4199
4200
/**
4201
 * FileAPI fallback to Flash
4202
 *
4203
 * @flash-developer  "Vladimir Demidov" <[email protected]>
4204
 */
4205
4206
/*global window, FileAPI */
4207
(function (window, jQuery, api) {
4208
    "use strict";
4209
4210
    var _each = api.each,
4211
        _cameraQueue = [];
4212
4213
4214
    if (api.support.flash && (api.media && !api.support.media)) {
4215
        (function () {
4216
4217
            function _wrap(fn) {
4218
                var id = fn.wid = api.uid();
4219
                api.Flash._fn[id] = fn;
4220
                return 'FileAPI.Flash._fn.' + id;
4221
            }
4222
4223
4224
            function _unwrap(fn) {
4225
                try {
4226
                    api.Flash._fn[fn.wid] = null;
4227
                    delete api.Flash._fn[fn.wid];
4228
                } catch (e) {
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...
4229
                }
4230
            }
4231
4232
            var flash = api.Flash;
4233
            api.extend(api.Flash, {
4234
4235
                patchCamera: function () {
4236
                    api.Camera.fallback = function (el, options, callback) {
4237
                        var camId = api.uid();
4238
                        api.log('FlashAPI.Camera.publish: ' + camId);
4239
                        flash.publish(el, camId, api.extend(options, {
4240
                            camera: true,
4241
                            onEvent: _wrap(function _(evt) {
4242
                                if (evt.type === 'camera') {
4243
                                    _unwrap(_);
4244
4245
                                    if (evt.error) {
4246
                                        api.log('FlashAPI.Camera.publish.error: ' + evt.error);
4247
                                        callback(evt.error);
4248
                                    } else {
4249
                                        api.log('FlashAPI.Camera.publish.success: ' + camId);
4250
                                        callback(null);
4251
                                    }
4252
                                }
4253
                            })
4254
                        }));
4255
                    };
4256
                    // Run
4257
                    _each(_cameraQueue, function (args) {
4258
                        api.Camera.fallback.apply(api.Camera, args);
4259
                    });
4260
                    _cameraQueue = [];
4261
4262
4263
                    // FileAPI.Camera:proto
4264
                    api.extend(api.Camera.prototype, {
4265
                        _id: function () {
4266
                            return this.video.id;
4267
                        },
4268
4269
                        start: function (callback) {
4270
                            var _this = this;
4271
                            flash.cmd(this._id(), 'camera.on', {
4272
                                callback: _wrap(function _(evt) {
4273
                                    _unwrap(_);
4274
4275
                                    if (evt.error) {
4276
                                        api.log('FlashAPI.camera.on.error: ' + evt.error);
4277
                                        callback(evt.error, _this);
4278
                                    } else {
4279
                                        api.log('FlashAPI.camera.on.success: ' + _this._id());
4280
                                        _this._active = true;
4281
                                        callback(null, _this);
4282
                                    }
4283
                                })
4284
                            });
4285
                        },
4286
4287
                        stop: function () {
4288
                            this._active = false;
4289
                            flash.cmd(this._id(), 'camera.off');
4290
                        },
4291
4292
                        shot: function () {
4293
                            api.log('FlashAPI.Camera.shot:', this._id());
4294
4295
                            var shot = api.Flash.cmd(this._id(), 'shot', {});
4296
                            shot.type = 'image/png';
4297
                            shot.flashId = this._id();
4298
                            shot.isShot = true;
4299
4300
                            return new api.Camera.Shot(shot);
4301
                        }
4302
                    });
4303
                }
4304
            });
4305
4306
            api.Camera.fallback = function () {
4307
                _cameraQueue.push(arguments);
4308
            };
4309
4310
        }());
4311
    }
4312
}(window, window.jQuery, FileAPI));
4313
if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }
4314