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-3.3.4/demo/war/js/FileAPI.js   F
last analyzed

Complexity

Total Complexity 732
Complexity/F 2.21

Size

Lines of Code 4228
Function Count 331

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2300
dl 0
loc 4228
rs 0.8
c 0
b 0
f 0
wmc 732
mnd 401
bc 401
fnc 331
bpm 1.2114
cpm 2.2113
noi 32

32 Functions

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

How to fix   Complexity   

Complexity

Complex classes like third-party/angularjs-modules-plugins/ng-file-upload-3.3.4/demo/war/js/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
(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
		/**
280
		 * FileAPI (core object)
281
		 */
282
		api = {
283
			version: '2.0.7',
284
285
			cors: false,
286
			html5: true,
287
			media: false,
288
			formData: true,
289
			multiPassResize: true,
290
291
			debug: false,
292
			pingUrl: false,
293
			multiFlash: false,
294
			flashAbortTimeout: 0,
295
			withCredentials: true,
296
297
			staticPath: './dist/',
298
299
			flashUrl: 0, // @default: './FileAPI.flash.swf'
300
			flashImageUrl: 0, // @default: './FileAPI.flash.image.swf'
301
302
			postNameConcat: function (name, idx){
303
				return	name + (idx != null ? '['+ idx +']' : '');
304
			},
305
306
			ext2mime: {
307
				  jpg:	'image/jpeg'
308
				, tif:	'image/tiff'
309
				, txt:	'text/plain'
310
			},
311
312
			// Fallback for flash
313
			accept: {
314
				  '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'
315
				, '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'
316
				, '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'
317
			},
318
319
			uploadRetry : 0,
320
			networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
321
322
			chunkSize : 0,
323
			chunkUploadRetry : 0,
324
			chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
325
326
			KB: _SIZE_CONST(1),
327
			MB: _SIZE_CONST(2),
328
			GB: _SIZE_CONST(3),
329
			TB: _SIZE_CONST(4),
330
331
			EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
332
333
			expando: 'fileapi' + (new Date).getTime(),
334
335
			uid: function (obj){
336
				return	obj
337
					? (obj[api.expando] = obj[api.expando] || api.uid())
338
					: (++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...
339
				;
340
			},
341
342
			log: function (){
343
				if( api.debug && window.console && console.log ){
344
					if( console.log.apply ){
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: 'filreader_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
	if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
1796
	if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
1797
	if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
1798
})(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...
1799
1800
/*global window, FileAPI, document */
1801
1802
(function (api, document, undef) {
1803
	'use strict';
1804
1805
	var
1806
		min = Math.min,
1807
		round = Math.round,
1808
		getCanvas = function () { return document.createElement('canvas'); },
1809
		support = false,
1810
		exifOrientation = {
1811
			  8:	270
1812
			, 3:	180
1813
			, 6:	90
1814
			, 7:	270
1815
			, 4:	180
1816
			, 5:	90
1817
		}
1818
	;
1819
1820
	try {
1821
		support = getCanvas().toDataURL('image/png').indexOf('data:image/png') > -1;
1822
	}
1823
	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...
1824
1825
1826
	function Image(file){
1827
		if( file instanceof Image ){
1828
			var img = new Image(file.file);
1829
			api.extend(img.matrix, file.matrix);
1830
			return	img;
1831
		}
1832
		else if( !(this instanceof Image) ){
1833
			return	new Image(file);
1834
		}
1835
1836
		this.file   = file;
1837
		this.size   = file.size || 100;
1838
1839
		this.matrix	= {
1840
			sx: 0,
1841
			sy: 0,
1842
			sw: 0,
1843
			sh: 0,
1844
			dx: 0,
1845
			dy: 0,
1846
			dw: 0,
1847
			dh: 0,
1848
			resize: 0, // min, max OR preview
1849
			deg: 0,
1850
			quality: 1, // jpeg quality
1851
			filter: 0
1852
		};
1853
	}
1854
1855
1856
	Image.prototype = {
1857
		image: true,
1858
		constructor: Image,
1859
1860
		set: function (attrs){
1861
			api.extend(this.matrix, attrs);
1862
			return	this;
1863
		},
1864
1865
		crop: function (x, y, w, h){
1866
			if( w === undef ){
1867
				w	= x;
1868
				h	= y;
1869
				x = y = 0;
1870
			}
1871
			return	this.set({ sx: x, sy: y, sw: w, sh: h || w });
1872
		},
1873
1874
		resize: function (w, h, strategy){
1875
			if( /min|max/.test(h) ){
1876
				strategy = h;
1877
				h = w;
1878
			}
1879
1880
			return	this.set({ dw: w, dh: h || w, resize: strategy });
1881
		},
1882
1883
		preview: function (w, h){
1884
			return	this.resize(w, h || w, 'preview');
1885
		},
1886
1887
		rotate: function (deg){
1888
			return	this.set({ deg: deg });
1889
		},
1890
1891
		filter: function (filter){
1892
			return	this.set({ filter: filter });
1893
		},
1894
1895
		overlay: function (images){
1896
			return	this.set({ overlay: images });
1897
		},
1898
1899
		clone: function (){
1900
			return	new Image(this);
1901
		},
1902
1903
		_load: function (image, fn){
1904
			var self = this;
1905
1906
			if( /img|video/i.test(image.nodeName) ){
1907
				fn.call(self, null, image);
1908
			}
1909
			else {
1910
				api.readAsImage(image, function (evt){
1911
					fn.call(self, evt.type != 'load', evt.result);
1912
				});
1913
			}
1914
		},
1915
1916
		_apply: function (image, fn){
1917
			var
1918
				  canvas = getCanvas()
1919
				, m = this.getMatrix(image)
1920
				, ctx = canvas.getContext('2d')
1921
				, width = image.videoWidth || image.width
1922
				, height = image.videoHeight || image.height
1923
				, deg = m.deg
1924
				, dw = m.dw
1925
				, dh = m.dh
1926
				, w = width
1927
				, h = height
1928
				, filter = m.filter
1929
				, copy // canvas copy
1930
				, buffer = image
1931
				, overlay = m.overlay
1932
				, queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
1933
				, renderImageToCanvas = api.renderImageToCanvas
1934
			;
1935
1936
			// Normalize angle
1937
			deg = deg - Math.floor(deg/360)*360;
1938
1939
			// For `renderImageToCanvas`
1940
			image._type = this.file.type;
1941
1942
			while(m.multipass && min(w/dw, h/dh) > 2 ){
1943
				w = (w/2 + 0.5)|0;
1944
				h = (h/2 + 0.5)|0;
1945
1946
				copy = getCanvas();
1947
				copy.width  = w;
1948
				copy.height = h;
1949
1950
				if( buffer !== image ){
1951
					renderImageToCanvas(copy, buffer, 0, 0, buffer.width, buffer.height, 0, 0, w, h);
1952
					buffer = copy;
1953
				}
1954
				else {
1955
					buffer = copy;
1956
					renderImageToCanvas(buffer, image, m.sx, m.sy, m.sw, m.sh, 0, 0, w, h);
1957
					m.sx = m.sy = m.sw = m.sh = 0;
1958
				}
1959
			}
1960
1961
1962
			canvas.width  = (deg % 180) ? dh : dw;
1963
			canvas.height = (deg % 180) ? dw : dh;
1964
1965
			canvas.type = m.type;
1966
			canvas.quality = m.quality;
1967
1968
			ctx.rotate(deg * Math.PI / 180);
1969
			renderImageToCanvas(ctx.canvas, buffer
1970
				, m.sx, m.sy
1971
				, m.sw || buffer.width
1972
				, m.sh || buffer.height
1973
				, (deg == 180 || deg == 270 ? -dw : 0)
1974
				, (deg == 90 || deg == 180 ? -dh : 0)
1975
				, dw, dh
1976
			);
1977
			dw = canvas.width;
1978
			dh = canvas.height;
1979
1980
			// Apply overlay
1981
			overlay && api.each([].concat(overlay), function (over){
1982
				queue.inc();
1983
				// preload
1984
				var img = new window.Image, fn = function (){
1985
					var
1986
						  x = over.x|0
1987
						, y = over.y|0
1988
						, w = over.w || img.width
1989
						, h = over.h || img.height
1990
						, rel = over.rel
1991
					;
1992
1993
					// center  |  right  |  left
1994
					x = (rel == 1 || rel == 4 || rel == 7) ? (dw - w + x)/2 : (rel == 2 || rel == 5 || rel == 8 ? dw - (w + x) : x);
1995
1996
					// center  |  bottom  |  top
1997
					y = (rel == 3 || rel == 4 || rel == 5) ? (dh - h + y)/2 : (rel >= 6 ? dh - (h + y) : y);
1998
1999
					api.event.off(img, 'error load abort', fn);
2000
2001
					try {
2002
						ctx.globalAlpha = over.opacity || 1;
2003
						ctx.drawImage(img, x, y, w, h);
2004
					}
2005
					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...
2006
2007
					queue.next();
2008
				};
2009
2010
				api.event.on(img, 'error load abort', fn);
2011
				img.src = over.src;
2012
2013
				if( img.complete ){
2014
					fn();
2015
				}
2016
			});
2017
2018
			if( filter ){
2019
				queue.inc();
2020
				Image.applyFilter(canvas, filter, queue.next);
2021
			}
2022
2023
			queue.check();
2024
		},
2025
2026
		getMatrix: function (image){
2027
			var
2028
				  m  = api.extend({}, this.matrix)
2029
				, sw = m.sw = m.sw || image.videoWidth || image.naturalWidth ||  image.width
2030
				, sh = m.sh = m.sh || image.videoHeight || image.naturalHeight || image.height
2031
				, dw = m.dw = m.dw || sw
2032
				, dh = m.dh = m.dh || sh
2033
				, sf = sw/sh, df = dw/dh
2034
				, strategy = m.resize
2035
			;
2036
2037
			if( strategy == 'preview' ){
2038
				if( dw != sw || dh != sh ){
2039
					// Make preview
2040
					var w, h;
2041
2042
					if( df >= sf ){
2043
						w	= sw;
2044
						h	= w / df;
2045
					} else {
2046
						h	= sh;
2047
						w	= h * df;
2048
					}
2049
2050
					if( w != sw || h != sh ){
2051
						m.sx	= ~~((sw - w)/2);
2052
						m.sy	= ~~((sh - h)/2);
2053
						sw		= w;
2054
						sh		= h;
2055
					}
2056
				}
2057
			}
2058
			else if( strategy ){
2059
				if( !(sw > dw || sh > dh) ){
2060
					dw = sw;
2061
					dh = sh;
2062
				}
2063
				else if( strategy == 'min' ){
2064
					dw = round(sf < df ? min(sw, dw) : dh*sf);
2065
					dh = round(sf < df ? dw/sf : min(sh, dh));
2066
				}
2067
				else {
2068
					dw = round(sf >= df ? min(sw, dw) : dh*sf);
2069
					dh = round(sf >= df ? dw/sf : min(sh, dh));
2070
				}
2071
			}
2072
2073
			m.sw = sw;
2074
			m.sh = sh;
2075
			m.dw = dw;
2076
			m.dh = dh;
2077
			m.multipass = api.multiPassResize;
2078
			return	m;
2079
		},
2080
2081
		_trans: function (fn){
2082
			this._load(this.file, function (err, image){
2083
				if( err ){
2084
					fn(err);
2085
				}
2086
				else {
2087
					try {
2088
						this._apply(image, fn);
2089
					} catch (err){
2090
						api.log('[err] FileAPI.Image.fn._apply:', err);
2091
						fn(err);
2092
					}
2093
				}
2094
			});
2095
		},
2096
2097
2098
		get: function (fn){
2099
			if( api.support.transform ){
2100
				var _this = this, matrix = _this.matrix;
2101
2102
				if( matrix.deg == 'auto' ){
2103
					api.getInfo(_this.file, function (err, info){
2104
						// rotate by exif orientation
2105
						matrix.deg = exifOrientation[info && info.exif && info.exif.Orientation] || 0;
2106
						_this._trans(fn);
2107
					});
2108
				}
2109
				else {
2110
					_this._trans(fn);
2111
				}
2112
			}
2113
			else {
2114
				fn('not_support_transform');
2115
			}
2116
2117
			return this;
2118
		},
2119
2120
2121
		toData: function (fn){
2122
			return this.get(fn);
2123
		}
2124
2125
	};
2126
2127
2128
	Image.exifOrientation = exifOrientation;
2129
2130
2131
	Image.transform = function (file, transform, autoOrientation, fn){
2132
		function _transform(err, img){
2133
			// img -- info object
2134
			var
2135
				  images = {}
2136
				, queue = api.queue(function (err){
2137
					fn(err, images);
2138
				})
2139
			;
2140
2141
			if( !err ){
2142
				api.each(transform, function (params, name){
2143
					if( !queue.isFail() ){
2144
						var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
2145
2146
						if( isFn ){
2147
							params(img, ImgTrans);
2148
						}
2149
						else if( params.width ){
2150
							ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
2151
						}
2152
						else {
2153
							if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
2154
								ImgTrans.resize(params.maxWidth, params.maxHeight, 'max');
2155
							}
2156
						}
2157
2158
						if( params.crop ){
2159
							var crop = params.crop;
2160
							ImgTrans.crop(crop.x|0, crop.y|0, crop.w || crop.width, crop.h || crop.height);
2161
						}
2162
2163
						if( params.rotate === undef && autoOrientation ){
2164
							params.rotate = 'auto';
2165
						}
2166
2167
						ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
2168
2169
						if( !isFn ){
2170
							ImgTrans.set({
2171
								  deg: params.rotate
2172
								, overlay: params.overlay
2173
								, filter: params.filter
2174
								, quality: params.quality || 1
2175
							});
2176
						}
2177
2178
						queue.inc();
2179
						ImgTrans.toData(function (err, image){
2180
							if( err ){
2181
								queue.fail();
2182
							}
2183
							else {
2184
								images[name] = image;
2185
								queue.next();
2186
							}
2187
						});
2188
					}
2189
				});
2190
			}
2191
			else {
2192
				queue.fail();
2193
			}
2194
		}
2195
2196
2197
		// @todo: Оло-ло, нужно рефакторить это место
2198
		if( file.width ){
2199
			_transform(false, file);
2200
		} else {
2201
			api.getInfo(file, _transform);
2202
		}
2203
	};
2204
2205
2206
	// @const
2207
	api.each(['TOP', 'CENTER', 'BOTTOM'], function (x, i){
2208
		api.each(['LEFT', 'CENTER', 'RIGHT'], function (y, j){
2209
			Image[x+'_'+y] = i*3 + j;
2210
			Image[y+'_'+x] = i*3 + j;
2211
		});
2212
	});
2213
2214
2215
	/**
2216
	 * Trabsform element to canvas
2217
	 *
2218
	 * @param    {Image|HTMLVideoElement}   el
2219
	 * @returns  {Canvas}
2220
	 */
2221
	Image.toCanvas = function(el){
2222
		var canvas		= document.createElement('canvas');
2223
		canvas.width	= el.videoWidth || el.width;
2224
		canvas.height	= el.videoHeight || el.height;
2225
		canvas.getContext('2d').drawImage(el, 0, 0);
2226
		return	canvas;
2227
	};
2228
2229
2230
	/**
2231
	 * Create image from DataURL
2232
	 * @param  {String}  dataURL
2233
	 * @param  {Object}  size
2234
	 * @param  {Function}  callback
2235
	 */
2236
	Image.fromDataURL = function (dataURL, size, callback){
2237
		var img = api.newImage(dataURL);
2238
		api.extend(img, size);
2239
		callback(img);
2240
	};
2241
2242
2243
	/**
2244
	 * Apply filter (caman.js)
2245
	 *
2246
	 * @param  {Canvas|Image}   canvas
2247
	 * @param  {String|Function}  filter
2248
	 * @param  {Function}  doneFn
2249
	 */
2250
	Image.applyFilter = function (canvas, filter, doneFn){
2251
		if( typeof filter == 'function' ){
2252
			filter(canvas, doneFn);
2253
		}
2254
		else if( window.Caman ){
2255
			// http://camanjs.com/guides/
2256
			window.Caman(canvas.tagName == 'IMG' ? Image.toCanvas(canvas) : canvas, function (){
2257
				if( typeof filter == 'string' ){
2258
					this[filter]();
2259
				}
2260
				else {
2261
					api.each(filter, function (val, method){
2262
						this[method](val);
2263
					}, this);
2264
				}
2265
				this.render(doneFn);
2266
			});
2267
		}
2268
	};
2269
2270
2271
	/**
2272
	 * For load-image-ios.js
2273
	 */
2274
	api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
2275
		try {
2276
			return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
2277
		} catch (ex) {
2278
			api.log('renderImageToCanvas failed');
2279
			throw ex;
2280
		}
2281
	};
2282
2283
2284
	// @export
2285
	api.support.canvas = api.support.transform = support;
2286
	api.Image = Image;
2287
})(FileAPI, document);
2288
2289
/*
2290
 * JavaScript Load Image iOS scaling fixes 1.0.3
2291
 * https://github.com/blueimp/JavaScript-Load-Image
2292
 *
2293
 * Copyright 2013, Sebastian Tschan
2294
 * https://blueimp.net
2295
 *
2296
 * iOS image scaling fixes based on
2297
 * https://github.com/stomita/ios-imagefile-megapixel
2298
 *
2299
 * Licensed under the MIT license:
2300
 * http://www.opensource.org/licenses/MIT
2301
 */
2302
2303
/*jslint nomen: true, bitwise: true */
2304
/*global FileAPI, window, document */
2305
2306
(function (factory) {
2307
	'use strict';
2308
	factory(FileAPI);
2309
}(function (loadImage) {
2310
    'use strict';
2311
2312
    // Only apply fixes on the iOS platform:
2313
    if (!window.navigator || !window.navigator.platform ||
2314
             !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
2315
        return;
2316
    }
2317
2318
    var originalRenderMethod = loadImage.renderImageToCanvas;
2319
2320
    // Detects subsampling in JPEG images:
2321
    loadImage.detectSubsampling = function (img) {
2322
        var canvas,
2323
            context;
2324
        if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
2325
            canvas = document.createElement('canvas');
2326
            canvas.width = canvas.height = 1;
2327
            context = canvas.getContext('2d');
2328
            context.drawImage(img, -img.width + 1, 0);
2329
            // subsampled image becomes half smaller in rendering size.
2330
            // check alpha channel value to confirm image is covering edge pixel or not.
2331
            // if alpha value is 0 image is not covering, hence subsampled.
2332
            return context.getImageData(0, 0, 1, 1).data[3] === 0;
2333
        }
2334
        return false;
2335
    };
2336
2337
    // Detects vertical squash in JPEG images:
2338
    loadImage.detectVerticalSquash = function (img, subsampled) {
2339
        var naturalHeight = img.naturalHeight || img.height,
2340
            canvas = document.createElement('canvas'),
2341
            context = canvas.getContext('2d'),
2342
            data,
2343
            sy,
2344
            ey,
2345
            py,
2346
            alpha;
2347
        if (subsampled) {
2348
            naturalHeight /= 2;
2349
        }
2350
        canvas.width = 1;
2351
        canvas.height = naturalHeight;
2352
        context.drawImage(img, 0, 0);
2353
        data = context.getImageData(0, 0, 1, naturalHeight).data;
2354
        // search image edge pixel position in case it is squashed vertically:
2355
        sy = 0;
2356
        ey = naturalHeight;
2357
        py = naturalHeight;
2358
        while (py > sy) {
2359
            alpha = data[(py - 1) * 4 + 3];
2360
            if (alpha === 0) {
2361
                ey = py;
2362
            } else {
2363
                sy = py;
2364
            }
2365
            py = (ey + sy) >> 1;
2366
        }
2367
        return (py / naturalHeight) || 1;
2368
    };
2369
2370
    // Renders image to canvas while working around iOS image scaling bugs:
2371
    // https://github.com/blueimp/JavaScript-Load-Image/issues/13
2372
    loadImage.renderImageToCanvas = function (
2373
        canvas,
2374
        img,
2375
        sourceX,
2376
        sourceY,
2377
        sourceWidth,
2378
        sourceHeight,
2379
        destX,
2380
        destY,
2381
        destWidth,
2382
        destHeight
2383
    ) {
2384
        if (img._type === 'image/jpeg') {
2385
            var context = canvas.getContext('2d'),
2386
                tmpCanvas = document.createElement('canvas'),
2387
                tileSize = 1024,
2388
                tmpContext = tmpCanvas.getContext('2d'),
2389
                subsampled,
2390
                vertSquashRatio,
2391
                tileX,
2392
                tileY;
2393
            tmpCanvas.width = tileSize;
2394
            tmpCanvas.height = tileSize;
2395
            context.save();
2396
            subsampled = loadImage.detectSubsampling(img);
2397
            if (subsampled) {
2398
                sourceX /= 2;
2399
                sourceY /= 2;
2400
                sourceWidth /= 2;
2401
                sourceHeight /= 2;
2402
            }
2403
            vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
2404
            if (subsampled || vertSquashRatio !== 1) {
2405
                sourceY *= vertSquashRatio;
2406
                destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
2407
                destHeight = Math.ceil(
2408
                    tileSize * destHeight / sourceHeight / vertSquashRatio
2409
                );
2410
                destY = 0;
2411
                tileY = 0;
2412
                while (tileY < sourceHeight) {
2413
                    destX = 0;
2414
                    tileX = 0;
2415
                    while (tileX < sourceWidth) {
2416
                        tmpContext.clearRect(0, 0, tileSize, tileSize);
2417
                        tmpContext.drawImage(
2418
                            img,
2419
                            sourceX,
2420
                            sourceY,
2421
                            sourceWidth,
2422
                            sourceHeight,
2423
                            -tileX,
2424
                            -tileY,
2425
                            sourceWidth,
2426
                            sourceHeight
2427
                        );
2428
                        context.drawImage(
2429
                            tmpCanvas,
2430
                            0,
2431
                            0,
2432
                            tileSize,
2433
                            tileSize,
2434
                            destX,
2435
                            destY,
2436
                            destWidth,
2437
                            destHeight
2438
                        );
2439
                        tileX += tileSize;
2440
                        destX += destWidth;
2441
                    }
2442
                    tileY += tileSize;
2443
                    destY += destHeight;
2444
                }
2445
                context.restore();
2446
                return canvas;
2447
            }
2448
        }
2449
        return originalRenderMethod(
2450
            canvas,
2451
            img,
2452
            sourceX,
2453
            sourceY,
2454
            sourceWidth,
2455
            sourceHeight,
2456
            destX,
2457
            destY,
2458
            destWidth,
2459
            destHeight
2460
        );
2461
    };
2462
2463
}));
2464
2465
/*global window, FileAPI */
2466
2467
(function (api, window){
2468
	"use strict";
2469
2470
	var
2471
		  document = window.document
2472
		, FormData = window.FormData
2473
		, Form = function (){ this.items = []; }
2474
		, encodeURIComponent = window.encodeURIComponent
2475
	;
2476
2477
2478
	Form.prototype = {
2479
2480
		append: function (name, blob, file, type){
2481
			this.items.push({
2482
				  name: name
2483
				, 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...
2484
				, file: blob && (file || blob.name)
2485
				, type:	blob && (type || blob.type)
2486
			});
2487
		},
2488
2489
		each: function (fn){
2490
			var i = 0, n = this.items.length;
2491
			for( ; i < n; i++ ){
2492
				fn.call(this, this.items[i]);
2493
			}
2494
		},
2495
2496
		toData: function (fn, options){
2497
		    // allow chunked transfer if we have only one file to send
2498
		    // flag is used below and in XHR._send
2499
		    options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1;
2500
2501
			if( !api.support.html5 ){
2502
				api.log('FileAPI.Form.toHtmlData');
2503
				this.toHtmlData(fn);
2504
			}
2505
			else if( !api.formData || this.multipart || !FormData ){
2506
				api.log('FileAPI.Form.toMultipartData');
2507
				this.toMultipartData(fn);
2508
			}
2509
			else if( options._chunked ){
2510
				api.log('FileAPI.Form.toPlainData');
2511
				this.toPlainData(fn);
2512
			}
2513
			else {
2514
				api.log('FileAPI.Form.toFormData');
2515
				this.toFormData(fn);
2516
			}
2517
		},
2518
2519
		_to: function (data, complete, next, arg){
2520
			var queue = api.queue(function (){
2521
				complete(data);
2522
			});
2523
2524
			this.each(function (file){
2525
				next(file, data, queue, arg);
2526
			});
2527
2528
			queue.check();
2529
		},
2530
2531
2532
		toHtmlData: function (fn){
2533
			this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){
2534
				var blob = file.blob, hidden;
2535
2536
				if( file.file ){
2537
					api.reset(blob, true);
2538
					// set new name
2539
					blob.name = file.name;
2540
					blob.disabled = false;
2541
					data.appendChild(blob);
2542
				}
2543
				else {
2544
					hidden = document.createElement('input');
2545
					hidden.name  = file.name;
2546
					hidden.type  = 'hidden';
2547
					hidden.value = blob;
2548
					data.appendChild(hidden);
2549
				}
2550
			});
2551
		},
2552
2553
		toPlainData: function (fn){
2554
			this._to({}, fn, function (file, data, queue){
2555
				if( file.file ){
2556
					data.type = file.file;
2557
				}
2558
2559
				if( file.blob.toBlob ){
2560
				    // canvas
2561
					queue.inc();
2562
					_convertFile(file, function (file, blob){
2563
						data.name = file.name;
2564
						data.file = blob;
2565
						data.size = blob.length;
2566
						data.type = file.type;
2567
						queue.next();
2568
					});
2569
				}
2570
				else if( file.file ){
2571
				    // file
2572
					data.name = file.blob.name;
2573
					data.file = file.blob;
2574
					data.size = file.blob.size;
2575
					data.type = file.type;
2576
				}
2577
				else {
2578
				    // additional data
2579
				    if( !data.params ){
2580
				        data.params = [];
2581
				    }
2582
				    data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob));
2583
				}
2584
2585
				data.start = -1;
2586
				data.end = data.file && data.file.FileAPIReadPosition || -1;
2587
				data.retry = 0;
2588
			});
2589
		},
2590
2591
		toFormData: function (fn){
2592
			this._to(new FormData, fn, function (file, data, queue){
2593
				if( file.blob && file.blob.toBlob ){
2594
					queue.inc();
2595
					_convertFile(file, function (file, blob){
2596
						data.append(file.name, blob, file.file);
2597
						queue.next();
2598
					});
2599
				}
2600
				else if( file.file ){
2601
					data.append(file.name, file.blob, file.file);
2602
				}
2603
				else {
2604
					data.append(file.name, file.blob);
2605
				}
2606
2607
				if( file.file ){
2608
					data.append('_'+file.name, file.file);
2609
				}
2610
			});
2611
		},
2612
2613
2614
		toMultipartData: function (fn){
2615
			this._to([], fn, function (file, data, queue, boundary){
2616
				queue.inc();
2617
				_convertFile(file, function (file, blob){
2618
					data.push(
2619
						  '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '')
2620
						+ (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '')
2621
						+ '\r\n'
2622
						+ '\r\n'+ (file.file ? blob : encodeURIComponent(blob))
2623
						+ '\r\n')
2624
					);
2625
					queue.next();
2626
				}, true);
2627
			}, api.expando);
2628
		}
2629
	};
2630
2631
2632
	function _convertFile(file, fn, useBinaryString){
2633
		var blob = file.blob, filename = file.file;
2634
2635
		if( filename ){
2636
			if( !blob.toDataURL ){
2637
				// The Blob is not an image.
2638
				api.readAsBinaryString(blob, function (evt){
2639
					if( evt.type == 'load' ){
2640
						fn(file, evt.result);
2641
					}
2642
				});
2643
				return;
2644
			}
2645
2646
			var
2647
				  mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' }
2648
				, type = mime[file.type] ? file.type : 'image/png'
2649
				, ext  = mime[type] || '.png'
2650
				, quality = blob.quality || 1
2651
			;
2652
2653
			if( !filename.match(new RegExp(ext+'$', 'i')) ){
2654
				// Does not change the current extension, but add a new one.
2655
				filename += ext.replace('?', '');
2656
			}
2657
2658
			file.file = filename;
2659
			file.type = type;
2660
2661
			if( !useBinaryString && blob.toBlob ){
2662
				blob.toBlob(function (blob){
2663
					fn(file, blob);
2664
				}, type, quality);
2665
			}
2666
			else {
2667
				fn(file, api.toBinaryString(blob.toDataURL(type, quality)));
2668
			}
2669
		}
2670
		else {
2671
			fn(file, blob);
2672
		}
2673
	}
2674
2675
2676
	// @export
2677
	api.Form = Form;
2678
})(FileAPI, window);
2679
2680
/*global window, FileAPI, Uint8Array */
2681
2682
(function (window, api){
2683
	"use strict";
2684
2685
	var
2686
		  noop = function (){}
2687
		, document = window.document
2688
2689
		, XHR = function (options){
2690
			this.uid = api.uid();
2691
			this.xhr = {
2692
				  abort: noop
2693
				, getResponseHeader: noop
2694
				, getAllResponseHeaders: noop
2695
			};
2696
			this.options = options;
2697
		},
2698
2699
		_xhrResponsePostfix = { '': 1, XML: 1, Text: 1, Body: 1 }
2700
	;
2701
2702
2703
	XHR.prototype = {
2704
		status: 0,
2705
		statusText: '',
2706
		constructor: XHR,
2707
2708
		getResponseHeader: function (name){
2709
			return this.xhr.getResponseHeader(name);
2710
		},
2711
2712
		getAllResponseHeaders: function (){
2713
			return this.xhr.getAllResponseHeaders() || {};
2714
		},
2715
2716
		end: function (status, statusText){
2717
			var _this = this, options = _this.options;
2718
2719
			_this.end		=
2720
			_this.abort		= noop;
2721
			_this.status	= status;
2722
2723
			if( statusText ){
2724
				_this.statusText = statusText;
2725
			}
2726
2727
			api.log('xhr.end:', status, statusText);
2728
			options.complete(status == 200 || status == 201 ? false : _this.statusText || 'unknown', _this);
2729
2730
			if( _this.xhr && _this.xhr.node ){
2731
				setTimeout(function (){
2732
					var node = _this.xhr.node;
2733
					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...
2734
					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...
2735
					window[_this.uid] = _this.xhr.node = null;
2736
				}, 9);
2737
			}
2738
		},
2739
2740
		abort: function (){
2741
			this.end(0, 'abort');
2742
2743
			if( this.xhr ){
2744
				this.xhr.aborted = true;
2745
				this.xhr.abort();
2746
			}
2747
		},
2748
2749
		send: function (FormData){
2750
			var _this = this, options = this.options;
2751
2752
			FormData.toData(function (data){
2753
				// Start uploading
2754
				options.upload(options, _this);
2755
				_this._send.call(_this, options, data);
2756
			}, options);
2757
		},
2758
2759
		_send: function (options, data){
2760
			var _this = this, xhr, uid = _this.uid, onloadFuncName = _this.uid + "Load", url = options.url;
2761
2762
			api.log('XHR._send:', data);
2763
2764
			if( !options.cache ){
2765
				// No cache
2766
				url += (~url.indexOf('?') ? '&' : '?') + api.uid();
2767
			}
2768
2769
			if( data.nodeName ){
2770
				var jsonp = options.jsonp;
2771
2772
				// prepare callback in GET
2773
				url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
2774
2775
				// legacy
2776
				options.upload(options, _this);
2777
2778
				var
2779
					onPostMessage = function (evt){
2780
						if( ~url.indexOf(evt.origin) ){
2781
							try {
2782
								var result = api.parseJSON(evt.data);
2783
								if( result.id == uid ){
2784
									complete(result.status, result.statusText, result.response);
2785
								}
2786
							} catch( err ){
2787
								complete(0, err.message);
2788
							}
2789
						}
2790
					},
2791
2792
					// jsonp-callack
2793
					complete = window[uid] = function (status, statusText, response){
2794
						_this.readyState	= 4;
2795
						_this.responseText	= response;
2796
						_this.end(status, statusText);
2797
2798
						api.event.off(window, 'message', onPostMessage);
2799
						window[uid] = xhr = transport = window[onloadFuncName] = null;
2800
					}
2801
				;
2802
2803
				_this.xhr.abort = function (){
2804
					try {
2805
						if( transport.stop ){ transport.stop(); }
2806
						else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
2807
						else { transport.contentWindow.document.execCommand('Stop'); }
2808
					}
2809
					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...
2810
					complete(0, "abort");
2811
				};
2812
2813
				api.event.on(window, 'message', onPostMessage);
2814
2815
				window[onloadFuncName] = function (){
2816
					try {
2817
						var
2818
							  win = transport.contentWindow
2819
							, doc = win.document
2820
							, result = win.result || api.parseJSON(doc.body.innerHTML)
2821
						;
2822
						complete(result.status, result.statusText, result.response);
2823
					} catch (e){
2824
						api.log('[transport.onload]', e);
2825
					}
2826
				};
2827
2828
				xhr = document.createElement('div');
2829
				xhr.innerHTML = '<form target="'+ uid +'" action="../../../../../ng-file-upload-3.3.4/demo/war/js/'+ url +'" method="POST" enctype="multipart/form-data" style="position: absolute; top: -1000px; overflow: hidden; width: 1px; height: 1px;">'
2830
							+ '<iframe name="'+ uid +'" src="javascript:false;" onload="' + onloadFuncName + '()"></iframe>'
2831
							+ (jsonp && (options.url.indexOf('=?') < 0) ? '<input value="'+ uid +'" name="'+jsonp+'" type="hidden"/>' : '')
2832
							+ '</form>'
2833
				;
2834
2835
				// get form-data & transport
2836
				var
2837
					  form = xhr.getElementsByTagName('form')[0]
2838
					, transport = xhr.getElementsByTagName('iframe')[0]
2839
				;
2840
2841
				form.appendChild(data);
2842
2843
				api.log(form.parentNode.innerHTML);
2844
2845
				// append to DOM
2846
				document.body.appendChild(xhr);
2847
2848
				// keep a reference to node-transport
2849
				_this.xhr.node = xhr;
2850
2851
				// send
2852
				_this.readyState = 2; // loaded
2853
				form.submit();
2854
				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...
2855
			}
2856
			else {
2857
				// Clean url
2858
				url = url.replace(/([a-z]+)=(\?)&?/i, '');
2859
2860
				// html5
2861
				if (this.xhr && this.xhr.aborted) {
2862
					api.log("Error: already aborted");
2863
					return;
2864
				}
2865
				xhr = _this.xhr = api.getXHR();
2866
2867
				if (data.params) {
2868
					url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
2869
				}
2870
2871
				xhr.open('POST', url, true);
2872
2873
				if( api.withCredentials ){
2874
					xhr.withCredentials = "true";
2875
				}
2876
2877
				if( !options.headers || !options.headers['X-Requested-With'] ){
2878
					xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2879
				}
2880
2881
				api.each(options.headers, function (val, key){
2882
					xhr.setRequestHeader(key, val);
2883
				});
2884
2885
2886
				if ( options._chunked ) {
2887
					// chunked upload
2888
					if( xhr.upload ){
2889
						xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
2890
							if (!data.retry) {
2891
								// show progress only for correct chunk uploads
2892
								options.progress({
2893
									  type:			evt.type
2894
									, total:		data.size
2895
									, loaded:		data.start + evt.loaded
2896
									, totalSize:	data.size
2897
								}, _this, options);
2898
							}
2899
						}, 100), false);
2900
					}
2901
2902
					xhr.onreadystatechange = function (){
2903
						var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10);
2904
2905
						_this.status     = xhr.status;
2906
						_this.statusText = xhr.statusText;
2907
						_this.readyState = xhr.readyState;
2908
2909
						if( xhr.readyState == 4 ){
2910
							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...
2911
								_this['response'+k]  = xhr['response'+k];
2912
							}
2913
							xhr.onreadystatechange = null;
2914
2915
							if (!xhr.status || xhr.status - 201 > 0) {
2916
								api.log("Error: " + xhr.status);
2917
								// some kind of error
2918
								// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
2919
								// up - server error
2920
								if (((!xhr.status && !xhr.aborted) || 500 == xhr.status || 416 == xhr.status) && ++data.retry <= options.chunkUploadRetry) {
2921
									// let's try again the same chunk
2922
									// only applicable for recoverable error codes 500 && 416
2923
									var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
2924
2925
									// inform about recoverable problems
2926
									options.pause(data.file, options);
2927
2928
									// smart restart if server reports about the last known byte
2929
									api.log("X-Last-Known-Byte: " + lkb);
2930
									if (lkb) {
2931
										data.end = lkb;
2932
									} else {
2933
										data.end = data.start - 1;
2934
										if (416 == xhr.status) {
2935
											data.end = data.end - options.chunkSize;
2936
										}
2937
									}
2938
2939
									setTimeout(function () {
2940
										_this._send(options, data);
2941
									}, delay);
2942
								} else {
2943
									// no mo retries
2944
									_this.end(xhr.status);
2945
								}
2946
							} else {
2947
								// success
2948
								data.retry = 0;
2949
2950
								if (data.end == data.size - 1) {
2951
									// finished
2952
									_this.end(xhr.status);
2953
								} else {
2954
									// next chunk
2955
2956
									// shift position if server reports about the last known byte
2957
									api.log("X-Last-Known-Byte: " + lkb);
2958
									if (lkb) {
2959
										data.end = lkb;
2960
									}
2961
									data.file.FileAPIReadPosition = data.end;
2962
2963
									setTimeout(function () {
2964
										_this._send(options, data);
2965
									}, 0);
2966
								}
2967
							}
2968
2969
							xhr = null;
2970
						}
2971
					};
2972
2973
					data.start = data.end + 1;
2974
					data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
2975
2976
					// Retrieve a slice of file
2977
					var
2978
						  file = data.file
2979
						, slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
2980
					;
2981
2982
					if( data.size && !slice.size ){
2983
						setTimeout(function (){
2984
							_this.end(-1);
2985
						});
2986
					} else {
2987
						xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
2988
						xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
2989
						xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
2990
2991
						xhr.send(slice);
2992
					}
2993
2994
					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...
2995
				} else {
2996
					// single piece upload
2997
					if( xhr.upload ){
2998
						// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
2999
						xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
3000
							options.progress(evt, _this, options);
3001
						}, 100), false);
3002
					}
3003
3004
					xhr.onreadystatechange = function (){
3005
						_this.status     = xhr.status;
3006
						_this.statusText = xhr.statusText;
3007
						_this.readyState = xhr.readyState;
3008
3009
						if( xhr.readyState == 4 ){
3010
							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...
3011
								_this['response'+k]  = xhr['response'+k];
3012
							}
3013
							xhr.onreadystatechange = null;
3014
3015
							if (!xhr.status || xhr.status > 201) {
3016
								api.log("Error: " + xhr.status);
3017
								if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
3018
									options.retry = (options.retry || 0) + 1;
3019
									var delay = api.networkDownRetryTimeout;
3020
3021
									// inform about recoverable problems
3022
									options.pause(options.file, options);
3023
3024
									setTimeout(function () {
3025
										_this._send(options, data);
3026
									}, delay);
3027
								} else {
3028
									//success
3029
									_this.end(xhr.status);
3030
								}
3031
							} else {
3032
								//success
3033
								_this.end(xhr.status);
3034
							}
3035
3036
							xhr = null;
3037
						}
3038
					};
3039
3040
					if( api.isArray(data) ){
3041
						// multipart
3042
						xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
3043
						var rawData = data.join('') +'--_'+ api.expando +'--';
3044
3045
						/** @namespace  xhr.sendAsBinary  https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
3046
						if( xhr.sendAsBinary ){
3047
							xhr.sendAsBinary(rawData);
3048
						}
3049
						else {
3050
							var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
3051
							xhr.send(new Uint8Array(bytes).buffer);
3052
3053
						}
3054
					} else {
3055
						// FormData
3056
						xhr.send(data);
3057
					}
3058
				}
3059
			}
3060
		}
3061
	};
3062
3063
3064
	// @export
3065
	api.XHR = XHR;
3066
})(window, FileAPI);
3067
3068
/**
3069
 * @class	FileAPI.Camera
3070
 * @author	RubaXa	<[email protected]>
3071
 * @support	Chrome 21+, FF 18+, Opera 12+
3072
 */
3073
3074
/*global window, FileAPI, jQuery */
3075
/** @namespace LocalMediaStream -- https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API#LocalMediaStream */
3076
(function (window, api){
3077
	"use strict";
3078
3079
	var
3080
		URL = window.URL || window.webkitURL,
3081
3082
		document = window.document,
3083
		navigator = window.navigator,
3084
3085
		getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia,
3086
3087
		html5 = !!getMedia
3088
	;
3089
3090
3091
	// Support "media"
3092
	api.support.media = html5;
3093
3094
3095
	var Camera = function (video){
3096
		this.video = video;
3097
	};
3098
3099
3100
	Camera.prototype = {
3101
		isActive: function (){
3102
			return	!!this._active;
3103
		},
3104
3105
3106
		/**
3107
		 * Start camera streaming
3108
		 * @param	{Function}	callback
3109
		 */
3110
		start: function (callback){
3111
			var
3112
				  _this = this
3113
				, video = _this.video
3114
				, _successId
3115
				, _failId
3116
				, _complete = function (err){
3117
					_this._active = !err;
3118
					clearTimeout(_failId);
3119
					clearTimeout(_successId);
3120
//					api.event.off(video, 'loadedmetadata', _complete);
3121
					callback && callback(err, _this);
3122
				}
3123
			;
3124
3125
			getMedia.call(navigator, { video: true }, function (stream/**LocalMediaStream*/){
3126
				// Success
3127
				_this.stream = stream;
3128
3129
//				api.event.on(video, 'loadedmetadata', function (){
3130
//					_complete(null);
3131
//				});
3132
3133
				// Set camera stream
3134
				video.src = URL.createObjectURL(stream);
3135
3136
				// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
3137
				// See crbug.com/110938.
3138
				_successId = setInterval(function (){
3139
					if( _detectVideoSignal(video) ){
3140
						_complete(null);
3141
					}
3142
				}, 1000);
3143
3144
				_failId = setTimeout(function (){
3145
					_complete('timeout');
3146
				}, 5000);
3147
3148
				// Go-go-go!
3149
				video.play();
3150
			}, _complete/*error*/);
3151
		},
3152
3153
3154
		/**
3155
		 * Stop camera streaming
3156
		 */
3157
		stop: function (){
3158
			try {
3159
				this._active = false;
3160
				this.video.pause();
3161
				this.stream.stop();
3162
			} 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...
3163
		},
3164
3165
3166
		/**
3167
		 * Create screenshot
3168
		 * @return {FileAPI.Camera.Shot}
3169
		 */
3170
		shot: function (){
3171
			return	new Shot(this.video);
3172
		}
3173
	};
3174
3175
3176
	/**
3177
	 * Get camera element from container
3178
	 *
3179
	 * @static
3180
	 * @param	{HTMLElement}	el
3181
	 * @return	{Camera}
3182
	 */
3183
	Camera.get = function (el){
3184
		return	new Camera(el.firstChild);
3185
	};
3186
3187
3188
	/**
3189
	 * Publish camera element into container
3190
	 *
3191
	 * @static
3192
	 * @param	{HTMLElement}	el
3193
	 * @param	{Object}		options
3194
	 * @param	{Function}		[callback]
3195
	 */
3196
	Camera.publish = function (el, options, callback){
3197
		if( typeof options == 'function' ){
3198
			callback = options;
3199
			options = {};
3200
		}
3201
3202
		// Dimensions of "camera"
3203
		options = api.extend({}, {
3204
			  width:	'100%'
3205
			, height:	'100%'
3206
			, start:	true
3207
		}, options);
3208
3209
3210
		if( el.jquery ){
3211
			// Extract first element, from jQuery collection
3212
			el = el[0];
3213
		}
3214
3215
3216
		var doneFn = function (err){
3217
			if( err ){
3218
				callback(err);
3219
			}
3220
			else {
3221
				// Get camera
3222
				var cam = Camera.get(el);
3223
				if( options.start ){
3224
					cam.start(callback);
3225
				}
3226
				else {
3227
					callback(null, cam);
3228
				}
3229
			}
3230
		};
3231
3232
3233
		el.style.width	= _px(options.width);
3234
		el.style.height	= _px(options.height);
3235
3236
3237
		if( api.html5 && html5 ){
3238
			// Create video element
3239
			var video = document.createElement('video');
3240
3241
			// Set dimensions
3242
			video.style.width	= _px(options.width);
3243
			video.style.height	= _px(options.height);
3244
3245
			// Clean container
3246
			if( window.jQuery ){
3247
				jQuery(el).empty();
3248
			} else {
3249
				el.innerHTML = '';
3250
			}
3251
3252
			// Add "camera" to container
3253
			el.appendChild(video);
3254
3255
			// end
3256
			doneFn();
3257
		}
3258
		else {
3259
			Camera.fallback(el, options, doneFn);
3260
		}
3261
	};
3262
3263
3264
	Camera.fallback = function (el, options, callback){
3265
		callback('not_support_camera');
3266
	};
3267
3268
3269
	/**
3270
	 * @class	FileAPI.Camera.Shot
3271
	 */
3272
	var Shot = function (video){
3273
		var canvas	= video.nodeName ? api.Image.toCanvas(video) : video;
3274
		var shot	= api.Image(canvas);
3275
		shot.type	= 'image/png';
3276
		shot.width	= canvas.width;
3277
		shot.height	= canvas.height;
3278
		shot.size	= canvas.width * canvas.height * 4;
3279
		return	shot;
3280
	};
3281
3282
3283
	/**
3284
	 * Add "px" postfix, if value is a number
3285
	 *
3286
	 * @private
3287
	 * @param	{*}  val
3288
	 * @return	{String}
3289
	 */
3290
	function _px(val){
3291
		return	val >= 0 ? val + 'px' : val;
3292
	}
3293
3294
3295
	/**
3296
	 * @private
3297
	 * @param	{HTMLVideoElement} video
3298
	 * @return	{Boolean}
3299
	 */
3300
	function _detectVideoSignal(video){
3301
		var canvas = document.createElement('canvas'), ctx, res = false;
3302
		try {
3303
			ctx = canvas.getContext('2d');
3304
			ctx.drawImage(video, 0, 0, 1, 1);
3305
			res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
3306
		}
3307
		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...
3308
		return	res;
3309
	}
3310
3311
3312
	// @export
3313
	Camera.Shot	= Shot;
3314
	api.Camera	= Camera;
3315
})(window, FileAPI);
3316
3317
/**
3318
 * FileAPI fallback to Flash
3319
 *
3320
 * @flash-developer  "Vladimir Demidov" <[email protected]>
3321
 */
3322
3323
/*global window, ActiveXObject, FileAPI */
3324
(function (window, jQuery, api) {
3325
	"use strict";
3326
3327
	var
3328
		  document = window.document
3329
		, location = window.location
3330
		, navigator = window.navigator
3331
		, _each = api.each
3332
	;
3333
3334
3335
	api.support.flash = (function (){
3336
		var mime = navigator.mimeTypes, has = false;
3337
3338
		if( navigator.plugins && typeof navigator.plugins['Shockwave Flash'] == 'object' ){
3339
			has	= navigator.plugins['Shockwave Flash'].description && !(mime && mime['application/x-shockwave-flash'] && !mime['application/x-shockwave-flash'].enabledPlugin);
3340
		}
3341
		else {
3342
			try {
3343
				has	= !!(window.ActiveXObject && new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
3344
			}
3345
			catch(er){
3346
				api.log('Flash -- does not supported.');
3347
			}
3348
		}
3349
3350
		if( has && /^file:/i.test(location) ){
3351
			api.log('[warn] Flash does not work on `file:` protocol.');
3352
		}
3353
3354
		return	has;
3355
	})();
3356
3357
3358
	   api.support.flash
3359
	&& (0
3360
		|| !api.html5 || !api.support.html5
3361
		|| (api.cors && !api.support.cors)
3362
		|| (api.media && !api.support.media)
3363
	)
3364
	&& (function (){
3365
		var
3366
			  _attr  = api.uid()
3367
			, _retry = 0
3368
			, _files = {}
3369
			, _rhttp = /^https?:/i
3370
3371
			, flash = {
3372
				_fn: {},
3373
3374
3375
				/**
3376
				 * Initialization & preload flash object
3377
				 */
3378
				init: function (){
3379
					var child = document.body && document.body.firstChild;
3380
3381
					if( child ){
3382
						do {
3383
							if( child.nodeType == 1 ){
3384
								api.log('FlashAPI.state: awaiting');
3385
3386
								var dummy = document.createElement('div');
3387
3388
								dummy.id = '_' + _attr;
3389
3390
								_css(dummy, {
3391
									  top: 1
3392
									, right: 1
3393
									, width: 5
3394
									, height: 5
3395
									, position: 'absolute'
3396
									, zIndex: 1e6+'' // set max zIndex
3397
								});
3398
3399
								child.parentNode.insertBefore(dummy, child);
3400
								flash.publish(dummy, _attr);
3401
3402
								return;
3403
							}
3404
						}
3405
						while( child = child.nextSibling );
3406
					}
3407
3408
					if( _retry < 10 ){
3409
						setTimeout(flash.init, ++_retry*50);
3410
					}
3411
				},
3412
3413
3414
				/**
3415
				 * Publish flash-object
3416
				 *
3417
				 * @param {HTMLElement} el
3418
				 * @param {String} id
3419
				 * @param {Object} [opts]
3420
				 */
3421
				publish: function (el, id, opts){
3422
					opts = opts || {};
3423
					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...
3424
						  id: id
3425
						, src: _getUrl(api.flashUrl, 'r=' + api.version)
3426
//						, src: _getUrl('http://v.demidov.boom.corp.mail.ru/uploaderfileapi/FlashFileAPI.swf?1')
3427
						, wmode: opts.camera ? '' : 'transparent'
3428
						, flashvars: 'callback=' + (opts.onEvent || 'FileAPI.Flash.onEvent')
3429
							+ '&flashId='+ id
3430
							+ '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
3431
							+ (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
3432
							+ '&timeout='+api.flashAbortTimeout
3433
							+ (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
3434
							+ '&debug='+(api.debug?"1":"")
3435
					}, opts);
3436
				},
3437
3438
3439
				ready: function (){
3440
					api.log('FlashAPI.state: ready');
3441
3442
					flash.ready = api.F;
3443
					flash.isReady = true;
3444
					flash.patch();
3445
					flash.patchCamera && flash.patchCamera();
3446
					api.event.on(document, 'mouseover', flash.mouseover);
3447
					api.event.on(document, 'click', function (evt){
3448
						if( flash.mouseover(evt) ){
3449
							evt.preventDefault
3450
								? evt.preventDefault()
3451
								: (evt.returnValue = true)
3452
							;
3453
						}
3454
					});
3455
				},
3456
3457
3458
				getEl: function (){
3459
					return	document.getElementById('_'+_attr);
3460
				},
3461
3462
3463
				getWrapper: function (node){
3464
					do {
3465
						if( /js-fileapi-wrapper/.test(node.className) ){
3466
							return	node;
3467
						}
3468
					}
3469
					while( (node = node.parentNode) && (node !== document.body) );
3470
				},
3471
				
3472
				disableMouseover: false,
3473
3474
				mouseover: function (evt){
3475
					if (!flash.disableMouseover) {
3476
						var target = api.event.fix(evt).target;
3477
	
3478
						if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
3479
							var
3480
								  state = target.getAttribute(_attr)
3481
								, wrapper = flash.getWrapper(target)
3482
							;
3483
	
3484
							if( api.multiFlash ){
3485
								// check state:
3486
								//   i — published
3487
								//   i — initialization
3488
								//   r — ready
3489
								if( state == 'i' || state == 'r' ){
3490
									// publish fail
3491
									return	false;
3492
								}
3493
								else if( state != 'p' ){
3494
									// set "init" state
3495
									target.setAttribute(_attr, 'i');
3496
	
3497
									var dummy = document.createElement('div');
3498
	
3499
									if( !wrapper ){
3500
										api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found');
3501
										return;
3502
									}
3503
	
3504
									_css(dummy, {
3505
										  top:    0
3506
										, left:   0
3507
										, width:  target.offsetWidth
3508
										, height: target.offsetHeight
3509
										, zIndex: 1e6+'' // set max zIndex
3510
										, position: 'absolute'
3511
									});
3512
	
3513
									wrapper.appendChild(dummy);
3514
									flash.publish(dummy, api.uid());
3515
	
3516
									// set "publish" state
3517
									target.setAttribute(_attr, 'p');
3518
								}
3519
	
3520
								return	true;
3521
							}
3522
							else if( wrapper ){
3523
								// Use one flash element
3524
								var box = _getDimensions(wrapper);
3525
								_css(flash.getEl(), box);
3526
	
3527
								// Set current input
3528
								flash.curInp = target;
3529
							}
3530
						}
3531
						else if( !/object|embed/i.test(target.nodeName) ){
3532
							_css(flash.getEl(), { top: 1, left: 1, width: 5, height: 5 });
3533
						}
3534
					}
3535
				},
3536
3537
				onEvent: function (evt){
3538
					var type = evt.type;
3539
					
3540
					if( type == 'ready' ){
3541
						try {
3542
							// set "ready" state
3543
							flash.getInput(evt.flashId).setAttribute(_attr, 'r');
3544
						} 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...
3545
						}
3546
3547
						flash.ready();
3548
						setTimeout(function (){ flash.mouseenter(evt); }, 50);
3549
						return	true;
3550
					}
3551
					else if( type === 'ping' ){
3552
						api.log('(flash -> js).ping:', [evt.status, evt.savedStatus], evt.error);
3553
					}
3554
					else if( type === 'log' ){
3555
						api.log('(flash -> js).log:', evt.target);
3556
					}
3557
					else if( type in flash ){
3558
						setTimeout(function (){
3559
							api.log('FlashAPI.event.'+evt.type+':', evt);
3560
							flash[type](evt);
3561
						}, 1);
3562
					}
3563
				},
3564
				mouseDown: function(evt) {
3565
					flash.disableMouseover = true;
3566
				},
3567
				cancel: function(evt) {
3568
					flash.disableMouseover = false;
3569
				},
3570
				mouseenter: function (evt){
3571
					var node = flash.getInput(evt.flashId);
3572
3573
					if( node ){
3574
						// Set multiple mode
3575
						flash.cmd(evt, 'multiple', node.getAttribute('multiple') != null);
3576
3577
3578
						// Set files filter
3579
						var accept = [], exts = {};
3580
3581
						_each((node.getAttribute('accept') || '').split(/,\s*/), function (mime){
3582
							api.accept[mime] && _each(api.accept[mime].split(' '), function (ext){
3583
								exts[ext] = 1;
3584
							});
3585
						});
3586
3587
						_each(exts, function (i, ext){
3588
							accept.push( ext );
3589
						});
3590
3591
						flash.cmd(evt, 'accept', accept.length ? accept.join(',')+','+accept.join(',').toUpperCase() : '*');
3592
					}
3593
				},
3594
3595
3596
				get: function (id){
3597
					return	document[id] || window[id] || document.embeds[id];
3598
				},
3599
3600
3601
				getInput: function (id){
3602
					if( api.multiFlash ){
3603
						try {
3604
							var node = flash.getWrapper(flash.get(id));
3605
							if( node ){
3606
								return node.getElementsByTagName('input')[0];
3607
							}
3608
						} catch (e){
3609
							api.log('[err] Can not find "input" by flashId:', id, e);
3610
						}
3611
					} else {
3612
						return	flash.curInp;
3613
					}
3614
				},
3615
3616
3617
				select: function (evt){
3618
					try {
3619
						var
3620
							  inp = flash.getInput(evt.flashId)
3621
							, uid = api.uid(inp)
3622
							, files = evt.target.files
3623
							, event
3624
						;
3625
						_each(files, function (file){
3626
							api.checkFileObj(file);
3627
						});
3628
	
3629
						_files[uid] = files;
3630
	
3631
						if( document.createEvent ){
3632
							event = document.createEvent('Event');
3633
							event.files = files;
3634
							event.initEvent('change', true, true);
3635
							inp.dispatchEvent(event);
3636
						}
3637
						else if( jQuery ){
3638
							jQuery(inp).trigger({ type: 'change', files: files });
3639
						}
3640
						else {
3641
							event = document.createEventObject();
3642
							event.files = files;
3643
							inp.fireEvent('onchange', event);
3644
						}
3645
					} finally {
3646
						flash.disableMouseover = false;
3647
					}
3648
				},
3649
3650
3651
				cmd: function (id, name, data, last){
3652
					try {
3653
						api.log('(js -> flash).'+name+':', data);
3654
						return flash.get(id.flashId || id).cmd(name, data);
3655
					} catch (e){
3656
						api.log('(js -> flash).onError:', e);
3657
						if( !last ){
3658
							// try again
3659
							setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
3660
						}
3661
					}
3662
				},
3663
3664
3665
				patch: function (){
3666
					api.flashEngine = true;
3667
3668
					// FileAPI
3669
					_inherit(api, {
3670
						getFiles: function (input, filter, callback){
3671
							if( callback ){
3672
								api.filterFiles(api.getFiles(input), filter, callback);
3673
								return null;
3674
							}
3675
3676
							var files = api.isArray(input) ? input : _files[api.uid(input.target || input.srcElement || input)];
3677
3678
3679
							if( !files ){
3680
								// Файлов нету, вызываем родительский метод
3681
								return	this.parent.apply(this, arguments);
3682
							}
3683
3684
3685
							if( filter ){
3686
								filter	= api.getFilesFilter(filter);
3687
								files	= api.filter(files, function (file){ return filter.test(file.name); });
3688
							}
3689
3690
							return	files;
3691
						},
3692
3693
3694
						getInfo: function (file, fn){
3695
							if( _isHtmlFile(file) ){
3696
								this.parent.apply(this, arguments);
3697
							}
3698
							else if( file.isShot ){
3699
								fn(null, file.info = {
3700
									width: file.width,
3701
									height: file.height
3702
								});
3703
							}
3704
							else {
3705
								if( !file.__info ){
3706
									var defer = file.__info = api.defer();
3707
3708
//									flash.cmd(file, 'getFileInfo', {
3709
//										  id: file.id
3710
//										, callback: _wrap(function _(err, info){
3711
//											_unwrap(_);
3712
//											defer.resolve(err, file.info = info);
3713
//										})
3714
//									});
3715
									defer.resolve(null, file.info = null);
3716
3717
								}
3718
3719
								file.__info.then(fn);
3720
							}
3721
						}
3722
					});
3723
3724
3725
					// FileAPI.Image
3726
					api.support.transform = true;
3727
					api.Image && _inherit(api.Image.prototype, {
3728
						get: function (fn, scaleMode){
3729
							this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
3730
							return this.parent(fn);
3731
						},
3732
3733
						_load: function (file, fn){
3734
							api.log('FlashAPI.Image._load:', file);
3735
3736
							if( _isHtmlFile(file) ){
3737
								this.parent.apply(this, arguments);
3738
							}
3739
							else {
3740
								var _this = this;
3741
								api.getInfo(file, function (err){
3742
									fn.call(_this, err, file);
3743
								});
3744
							}
3745
						},
3746
3747
						_apply: function (file, fn){
3748
							api.log('FlashAPI.Image._apply:', file);
3749
3750
							if( _isHtmlFile(file) ){
3751
								this.parent.apply(this, arguments);
3752
							}
3753
							else {
3754
								var m = this.getMatrix(file.info), doneFn = fn;
3755
3756
								flash.cmd(file, 'imageTransform', {
3757
									  id: file.id
3758
									, matrix: m
3759
									, callback: _wrap(function _(err, base64){
3760
										api.log('FlashAPI.Image._apply.callback:', err);
3761
										_unwrap(_);
3762
3763
										if( err ){
3764
											doneFn(err);
3765
										}
3766
										else if( !api.support.html5 && (!api.support.dataURI || base64.length > 3e4) ){
3767
											_makeFlashImage({
3768
												  width:	(m.deg % 180) ? m.dh : m.dw
3769
												, height:	(m.deg % 180) ? m.dw : m.dh
3770
												, scale:	m.scaleMode
3771
											}, base64, doneFn);
3772
										}
3773
										else {
3774
											if( m.filter ){
3775
												doneFn = function (err, img){
3776
													if( err ){
3777
														fn(err);
3778
													}
3779
													else {
3780
														api.Image.applyFilter(img, m.filter, function (){
3781
															fn(err, this.canvas);
3782
														});
3783
													}
3784
												};
3785
											}
3786
3787
											api.newImage('data:'+ file.type +';base64,'+ base64, doneFn);
3788
										}
3789
									})
3790
								});
3791
							}
3792
						},
3793
3794
						toData: function (fn){
3795
							var
3796
								  file = this.file
3797
								, info = file.info
3798
								, matrix = this.getMatrix(info)
3799
							;
3800
							api.log('FlashAPI.Image.toData');
3801
3802
							if( _isHtmlFile(file) ){
3803
								this.parent.apply(this, arguments);
3804
							}
3805
							else {
3806
								if( matrix.deg == 'auto' ){
3807
									matrix.deg = api.Image.exifOrientation[info && info.exif && info.exif.Orientation] || 0;
3808
								}
3809
3810
								fn.call(this, !file.info, {
3811
									  id:		file.id
3812
									, flashId:	file.flashId
3813
									, name:		file.name
3814
									, type:		file.type
3815
									, matrix:	matrix
3816
								});
3817
							}
3818
						}
3819
					});
3820
3821
3822
					api.Image && _inherit(api.Image, {
3823
						fromDataURL: function (dataURL, size, callback){
3824
							if( !api.support.dataURI || dataURL.length > 3e4 ){
3825
								_makeFlashImage(
3826
									  api.extend({ scale: 'exactFit' }, size)
3827
									, dataURL.replace(/^data:[^,]+,/, '')
3828
									, function (err, el){ callback(el); }
3829
								);
3830
							}
3831
							else {
3832
								this.parent(dataURL, size, callback);
3833
							}
3834
						}
3835
					});
3836
3837
					// FileAPI.Form
3838
					_inherit(api.Form.prototype, {
3839
						toData: function (fn){
3840
							var items = this.items, i = items.length;
3841
3842
							for( ; i--; ){
3843
								if( items[i].file && _isHtmlFile(items[i].blob) ){
3844
									return this.parent.apply(this, arguments);
3845
								}
3846
							}
3847
3848
							api.log('FlashAPI.Form.toData');
3849
							fn(items);
3850
						}
3851
					});
3852
3853
3854
					// FileAPI.XHR
3855
					_inherit(api.XHR.prototype, {
3856
						_send: function (options, formData){
3857
							if(
3858
								   formData.nodeName
3859
								|| formData.append && api.support.html5
3860
								|| api.isArray(formData) && (typeof formData[0] === 'string')
3861
							){
3862
								// HTML5, Multipart or IFrame
3863
								return	this.parent.apply(this, arguments);
3864
							}
3865
3866
3867
							var
3868
								  data = {}
3869
								, files = {}
3870
								, _this = this
3871
								, flashId
3872
								, fileId
3873
							;
3874
3875
							_each(formData, function (item){
3876
								if( item.file ){
3877
									files[item.name] = item = _getFileDescr(item.blob);
3878
									fileId  = item.id;
3879
									flashId = item.flashId;
3880
								}
3881
								else {
3882
									data[item.name] = item.blob;
3883
								}
3884
							});
3885
3886
							if( !fileId ){
3887
								flashId = _attr;
3888
							}
3889
3890
							if( !flashId ){
3891
								api.log('[err] FlashAPI._send: flashId -- undefined');
3892
								return this.parent.apply(this, arguments);
3893
							}
3894
							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...
3895
								api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
3896
							}
3897
3898
							_this.xhr = {
3899
								headers: {},
3900
								abort: function (){ flash.cmd(flashId, 'abort', { id: fileId }); },
3901
								getResponseHeader: function (name){ return this.headers[name]; },
3902
								getAllResponseHeaders: function (){ return this.headers; }
3903
							};
3904
3905
							var queue = api.queue(function (){
3906
								flash.cmd(flashId, 'upload', {
3907
									  url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
3908
									, data: data
3909
									, files: fileId ? files : null
3910
									, headers: options.headers || {}
3911
									, callback: _wrap(function upload(evt){
3912
										var type = evt.type, result = evt.result;
3913
3914
										api.log('FlashAPI.upload.'+type);
3915
3916
										if( type == 'progress' ){
3917
											evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
3918
											evt.lengthComputable = true;
3919
											options.progress(evt);
3920
										}
3921
										else if( type == 'complete' ){
3922
											_unwrap(upload);
3923
3924
											if( typeof result == 'string' ){
3925
												_this.responseText	= result.replace(/%22/g, "\"").replace(/%5c/g, "\\").replace(/%26/g, "&").replace(/%25/g, "%");
3926
											}
3927
3928
											_this.end(evt.status || 200);
3929
										}
3930
										else if( type == 'abort' || type == 'error' ){
3931
											_this.end(evt.status || 0, evt.message);
3932
											_unwrap(upload);
3933
										}
3934
									})
3935
								});
3936
							});
3937
3938
3939
							// #2174: FileReference.load() call while FileReference.upload() or vice versa
3940
							_each(files, function (file){
3941
								queue.inc();
3942
								api.getInfo(file, queue.next);
3943
							});
3944
3945
							queue.check();
3946
						}
3947
					});
3948
				}
3949
			}
3950
		;
3951
3952
3953
		function _makeFlashHTML(opts){
3954
			return ('<object id="#id#" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'">'
3955
				+ '<param name="movie" value="#src#" />'
3956
				+ '<param name="flashvars" value="#flashvars#" />'
3957
				+ '<param name="swliveconnect" value="true" />'
3958
				+ '<param name="allowscriptaccess" value="always" />'
3959
				+ '<param name="allownetworking" value="all" />'
3960
				+ '<param name="menu" value="false" />'
3961
				+ '<param name="wmode" value="#wmode#" />'
3962
				+ '<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>'
3963
				+ '</object>').replace(/#(\w+)#/ig, function (a, name){ return opts[name]; })
3964
			;
3965
		}
3966
3967
3968
		function _css(el, css){
3969
			if( el && el.style ){
3970
				var key, val;
3971
				for( key in css ){
3972
					val = css[key];
3973
					if( typeof val == 'number' ){
3974
						val += 'px';
3975
					}
3976
					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...
3977
				}
3978
				
3979
			}
3980
		}
3981
3982
3983
		function _inherit(obj, methods){
3984
			_each(methods, function (fn, name){
3985
				var prev = obj[name];
3986
				obj[name] = function (){
3987
					this.parent = prev;
3988
					return fn.apply(this, arguments);
3989
				};
3990
			});
3991
		}
3992
3993
		function _isHtmlFile(file){
3994
			return	file && !file.flashId;
3995
		}
3996
3997
		function _wrap(fn){
3998
			var id = fn.wid = api.uid();
3999
			flash._fn[id] = fn;
4000
			return	'FileAPI.Flash._fn.'+id;
4001
		}
4002
4003
4004
		function _unwrap(fn){
4005
			try {
4006
				flash._fn[fn.wid] = null;
4007
				delete	flash._fn[fn.wid];
4008
			}
4009
			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...
4010
		}
4011
4012
4013
		function _getUrl(url, params){
4014
			if( !_rhttp.test(url) ){
4015
				if( /^\.\//.test(url) || '/' != url.charAt(0) ){
4016
					var path = location.pathname;
4017
					path = path.substr(0, path.lastIndexOf('/'));
4018
					url = (path +'/'+ url).replace('/./', '/');
4019
				}
4020
4021
				if( '//' != url.substr(0, 2) ){
4022
					url = '//' + location.host + url;
4023
				}
4024
4025
				if( !_rhttp.test(url) ){
4026
					url = location.protocol + url;
4027
				}
4028
			}
4029
4030
			if( params ){
4031
				url += (/\?/.test(url) ? '&' : '?') + params;
4032
			}
4033
4034
			return	url;
4035
		}
4036
4037
4038
		function _makeFlashImage(opts, base64, fn){
4039
			var
4040
				  key
4041
				, flashId = api.uid()
4042
				, el = document.createElement('div')
4043
				, attempts = 10
4044
			;
4045
4046
			for( key in opts ){
4047
				el.setAttribute(key, opts[key]);
4048
				el[key] = opts[key];
4049
			}
4050
4051
			_css(el, opts);
4052
4053
			opts.width	= '100%';
4054
			opts.height	= '100%';
4055
4056
			el.innerHTML = _makeFlashHTML(api.extend({
4057
				  id: flashId
4058
				, src: _getUrl(api.flashImageUrl, 'r='+ api.uid())
4059
				, wmode: 'opaque'
4060
				, flashvars: 'scale='+ opts.scale +'&callback='+_wrap(function _(){
4061
					_unwrap(_);
4062
					if( --attempts > 0 ){
4063
						_setImage();
4064
					}
4065
					return true;
4066
				})
4067
			}, opts));
4068
4069
			function _setImage(){
4070
				try {
4071
					// Get flash-object by id
4072
					var img = flash.get(flashId);
4073
					img.setImage(base64);
4074
				} catch (e){
4075
					api.log('[err] FlashAPI.Preview.setImage -- can not set "base64":', e);
4076
				}
4077
			}
4078
4079
			fn(false, el);
4080
			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...
4081
		}
4082
4083
4084
		function _getFileDescr(file){
4085
			return	{
4086
				  id: file.id
4087
				, name: file.name
4088
				, matrix: file.matrix
4089
				, flashId: file.flashId
4090
			};
4091
		}
4092
4093
4094
		function _getDimensions(el){
4095
			var
4096
				  box = el.getBoundingClientRect()
0 ignored issues
show
Unused Code introduced by
The variable box seems to be never used. Consider removing it.
Loading history...
4097
				, body = document.body
4098
				, 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...
4099
			;
4100
			
4101
			function getOffset(obj) {
4102
			    var left, top;
4103
			    left = top = 0;
4104
			    if (obj.offsetParent) {
4105
			        do {
4106
			            left += obj.offsetLeft;
4107
			            top  += obj.offsetTop;
4108
			        } while (obj = obj.offsetParent);
4109
			    }
4110
			    return {
4111
			        left : left,
4112
			        top : top
4113
			    };
4114
			};
4115
			
4116
			return {
4117
				  top:		getOffset(el).top
4118
				, left:		getOffset(el).left
4119
				, width:	el.offsetWidth
4120
				, height:	el.offsetHeight
4121
			};
4122
		}
4123
4124
		// @export
4125
		api.Flash = flash;
4126
4127
4128
		// Check dataURI support
4129
		api.newImage('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', function (err, img){
4130
			api.support.dataURI = !(img.width != 1 || img.height != 1);
4131
			flash.init();
4132
		});
4133
	})();
4134
})(window, window.jQuery, FileAPI);
4135
4136
/**
4137
 * FileAPI fallback to Flash
4138
 *
4139
 * @flash-developer  "Vladimir Demidov" <[email protected]>
4140
 */
4141
4142
/*global window, FileAPI */
4143
(function (window, jQuery, api) {
4144
    "use strict";
4145
4146
    var _each = api.each,
4147
        _cameraQueue = [];
4148
4149
4150
    if (api.support.flash && (api.media && !api.support.media)) {
4151
        (function () {
4152
4153
            function _wrap(fn) {
4154
                var id = fn.wid = api.uid();
4155
                api.Flash._fn[id] = fn;
4156
                return 'FileAPI.Flash._fn.' + id;
4157
            }
4158
4159
4160
            function _unwrap(fn) {
4161
                try {
4162
                    api.Flash._fn[fn.wid] = null;
4163
                    delete api.Flash._fn[fn.wid];
4164
                } 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...
4165
                }
4166
            }
4167
4168
            var flash = api.Flash;
4169
            api.extend(api.Flash, {
4170
4171
                patchCamera: function () {
4172
                    api.Camera.fallback = function (el, options, callback) {
4173
                        var camId = api.uid();
4174
                        api.log('FlashAPI.Camera.publish: ' + camId);
4175
                        flash.publish(el, camId, api.extend(options, {
4176
                            camera: true,
4177
                            onEvent: _wrap(function _(evt) {
4178
                                if (evt.type === 'camera') {
4179
                                    _unwrap(_);
4180
4181
                                    if (evt.error) {
4182
                                        api.log('FlashAPI.Camera.publish.error: ' + evt.error);
4183
                                        callback(evt.error);
4184
                                    } else {
4185
                                        api.log('FlashAPI.Camera.publish.success: ' + camId);
4186
                                        callback(null);
4187
                                    }
4188
                                }
4189
                            })
4190
                        }));
4191
                    };
4192
                    // Run
4193
                    _each(_cameraQueue, function (args) {
4194
                        api.Camera.fallback.apply(api.Camera, args);
4195
                    });
4196
                    _cameraQueue = [];
4197
4198
4199
                    // FileAPI.Camera:proto
4200
                    api.extend(api.Camera.prototype, {
4201
                        _id: function () {
4202
                            return this.video.id;
4203
                        },
4204
4205
                        start: function (callback) {
4206
                            var _this = this;
4207
                            flash.cmd(this._id(), 'camera.on', {
4208
                                callback: _wrap(function _(evt) {
4209
                                    _unwrap(_);
4210
4211
                                    if (evt.error) {
4212
                                        api.log('FlashAPI.camera.on.error: ' + evt.error);
4213
                                        callback(evt.error, _this);
4214
                                    } else {
4215
                                        api.log('FlashAPI.camera.on.success: ' + _this._id());
4216
                                        _this._active = true;
4217
                                        callback(null, _this);
4218
                                    }
4219
                                })
4220
                            });
4221
                        },
4222
4223
                        stop: function () {
4224
                            this._active = false;
4225
                            flash.cmd(this._id(), 'camera.off');
4226
                        },
4227
4228
                        shot: function () {
4229
                            api.log('FlashAPI.Camera.shot:', this._id());
4230
4231
                            var shot = api.Flash.cmd(this._id(), 'shot', {});
4232
                            shot.type = 'image/png';
4233
                            shot.flashId = this._id();
4234
                            shot.isShot = true;
4235
4236
                            return new api.Camera.Shot(shot);
4237
                        }
4238
                    });
4239
                }
4240
            });
4241
4242
            api.Camera.fallback = function () {
4243
                _cameraQueue.push(arguments);
4244
            };
4245
4246
        }());
4247
    }
4248
}(window, window.jQuery, FileAPI));
4249
if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }