Completed
Push — master ( 5bf5a9...80b817 )
by Rain
01:36
created

vendors/jua/_/jua.js   F

Complexity

Total Complexity 267
Complexity/F 3.22

Size

Lines of Code 1232
Function Count 83

Duplication

Duplicated Lines 404
Ratio 32.79 %

Importance

Changes 0
Metric Value
cc 0
wmc 267
nc 0
mnd 7
bc 185
fnc 83
dl 404
loc 1232
rs 2.4
bpm 2.2289
cpm 3.2168
noi 18
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complexity

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

Complex classes like vendors/jua/_/jua.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
(function(){function a(a){function l(){if(g&&d<a){var b=g,c=b[0],f=Array.prototype.slice.call(b,1),m=b.index;g===h?g=h=null:g=g.next,++d,f.push(function(a,b){--d;if(i)return;a?e&&k(i=a,e=j=g=h=null):(j[m]=b,--e?l():k(null,j))}),c.apply(null,f)}}var c={},d=0,e=0,f=-1,g,h,i=null,j=[],k=b;return arguments.length<1&&(a=Infinity),c.defer=function(){if(!i){var a=arguments;a.index=++f,h?(h.next=a,h=h.next):g=h=a,++e,l()}return c},c.await=function(a){return k=a,e||k(i,j),c},c}function b(){}typeof module=="undefined"?self.queue=a:module.exports=a,a.version="0.0.2"})();(function ($, window, queue) {
2
3
	'use strict';
4
	
5
	/**
6
	 * @param {*} mValue
7
	 * @return {boolean}
8
	 */
9
	function isUndefined(mValue)
10
	{
11
		return 'undefined' === typeof mValue;
12
	}
13
	
14
	/**
15
	 * @param {Object} mObjectFirst
16
	 * @param {Object=} mObjectSecond
17
	 * @return {Object}
18
	 */
19
	function extend(mObjectFirst, mObjectSecond)
20
	{
21
		if (mObjectSecond)
22
		{
23
			for (var sProp in mObjectSecond)
24
			{
25
				if (mObjectSecond.hasOwnProperty(sProp))
26
				{
27
					mObjectFirst[sProp] = mObjectSecond[sProp];
28
				}
29
			}
30
		}
31
	
32
		return mObjectFirst;
33
	}
34
	
35
	/**
36
	 * @param {*} oParent
37
	 * @param {*} oDescendant
38
	 *
39
	 * @return {boolean}
40
	 */
41
	function contains(oParent, oDescendant)
42
	{
43
		var bResult = false;
44
		if (oParent && oDescendant)
45
		{
46
			if (oParent === oDescendant)
47
			{
48
				bResult = true;
49
			}
50
			else if (oParent.contains)
51
			{
52
				bResult = oParent.contains(oDescendant);
53
			}
54
			else
55
			{
56
				/*jshint bitwise: false*/
57
				bResult = oDescendant.compareDocumentPosition ?
58
					!!(oDescendant.compareDocumentPosition(oParent) & 8) : false;
59
				/*jshint bitwise: true*/
60
			}
61
		}
62
	
63
		return bResult;
64
	}
65
	
66
	function mainClearTimeout(iTimer)
67
	{
68
		if (0 < iTimer)
69
		{
70
			clearTimeout(iTimer);
71
		}
72
		
73
		iTimer = 0;
74
	}
75
	
76
	/**
77
	 * @param {Event} oEvent
78
	 * @return {?Event}
79
	 */
80
	function getEvent(oEvent)
81
	{
82
		oEvent = (oEvent && (oEvent.originalEvent ?
83
			oEvent.originalEvent : oEvent)) || window.event;
84
	
85
		return oEvent.dataTransfer ? oEvent : null;
86
	}
87
	
88
	/**
89
	 * @param {Object} oValues
90
	 * @param {string} sKey
91
	 * @param {?} mDefault
92
	 * @return {?}
93
	 */
94
	function getValue(oValues, sKey, mDefault)
95
	{
96
		return (!oValues || !sKey || isUndefined(oValues[sKey])) ? mDefault : oValues[sKey];
97
	}
98
	
99
	/**
100
	 * @param {Object} oOwner
101
	 * @param {string} sPublicName
102
	 * @param {*} mObject
103
	 */
104
	function setValue(oOwner, sPublicName, mObject)
105
	{
106
		oOwner[sPublicName] = mObject;
107
	}
108
	
109
	/**
110
	 * @param {*} aData
111
	 * @return {boolean}
112
	 */
113
	function isNonEmptyArray(aData)
114
	{
115
		return aData && aData.length && 0 < aData.length ? true : false;
116
	}
117
	
118
	/**
119
	 * @param {*} mValue
120
	 * @return {number}
121
	 */
122
	function pInt(mValue)
123
	{
124
		return parseInt(mValue || 0, 10);
125
	}
126
	
127
	/**
128
	 * @param {Function} fFunction
129
	 * @param {Object=} oScope
130
	 * @return {Function}
131
	 */
132
	function scopeBind(fFunction, oScope)
133
	{
134
		return function () {
135
			return fFunction.apply(isUndefined(oScope) ? null : oScope,
136
				Array.prototype.slice.call(arguments));
137
		};
138
	}
139
	
140
	/**
141
	 * @param {number=} iLen
142
	 * @return {string}
143
	 */
144
	function fakeMd5(iLen)
145
	{
146
		var
147
			sResult = '',
148
			sLine = '0123456789abcdefghijklmnopqrstuvwxyz'
149
		;
150
	
151
		iLen = isUndefined(iLen) ? 32 : pInt(iLen);
152
	
153
		while (sResult.length < iLen)
154
		{
155
			sResult += sLine.substr(Math.round(Math.random() * sLine.length), 1);
156
		}
157
	
158
		return sResult;
159
	}
160
	
161
	/**
162
	 * @return {string}
163
	 */
164
	function getNewUid()
165
	{
166
		return 'jua-uid-' + fakeMd5(16) + '-' + (new Date()).getTime().toString();
167
	}
168
	
169
	/**
170
	 * @param {*} oFile
171
	 * @param {string=} sPath
172
	 * @return {Object}
173
	 */
174
	function getDataFromFile(oFile, sPath)
175
	{
176
		var
177
			sFileName = isUndefined(oFile.fileName) ? (isUndefined(oFile.name) ? null : oFile.name) : oFile.fileName,
178
			iSize = isUndefined(oFile.fileSize) ? (isUndefined(oFile.size) ? null : oFile.size) : oFile.fileSize,
179
			sType = isUndefined(oFile.type) ? null : oFile.type
180
		;
181
	
182
		return {
183
			'FileName': sFileName,
184
			'Size': iSize,
185
			'Type': sType,
186
			'Folder': isUndefined(sPath) ? '' : sPath,
187
			'File' : oFile
188
		};
189
	}
190
	
191
	/**
192
	 * @param {*} aItems
193
	 * @param {Function} fFileCallback
194
	 * @param {boolean=} bEntry = false
195
	 * @param {boolean=} bAllowFolderDragAndDrop = true
196
	 * @param {number=} iLimit = 20
197
	 * @param {Function=} fLimitCallback
198
	 */
199
	function getDataFromFiles(aItems, fFileCallback, bEntry, bAllowFolderDragAndDrop, iLimit, fLimitCallback)
200
	{
201
		var
202
			iInputLimit = 0,
203
			iLen = 0,
204
			iIndex = 0,
205
			oItem = null,
206
			oEntry = null,
207
			bUseLimit = false,
208
			bCallLimit = false,
209
			fTraverseFileTree = function (oItem, sPath, fCallback, fLimitCallbackProxy) {
210
	
211
				if (oItem && !isUndefined(oItem['name']))
212
				{
213
					sPath = sPath || '';
214
					if (oItem['isFile'])
215
					{
216
						oItem.file(function (oFile) {
217
							if (!bUseLimit || 0 <= --iLimit)
218
							{
219
								fCallback(getDataFromFile(oFile, sPath));
220
							}
221
							else if (bUseLimit && !bCallLimit)
222
							{
223
								if (0 > iLimit && fLimitCallback)
224
								{
225
									bCallLimit = true;
226
									fLimitCallback(iInputLimit);
227
								}
228
							}
229
						});
230
					}
231
					else if (bAllowFolderDragAndDrop && oItem['isDirectory'] && oItem['createReader'])
232
					{
233
						var
234
							oDirReader = oItem['createReader'](),
235
							iIndex = 0,
236
							iLen = 0
237
						;
238
	
239
						if (oDirReader && oDirReader['readEntries'])
240
						{
241
							oDirReader['readEntries'](function (aEntries) {
242
								if (aEntries && isNonEmptyArray(aEntries))
243
								{
244
									for (iIndex = 0, iLen = aEntries.length; iIndex < iLen; iIndex++)
245
									{
246
										fTraverseFileTree(aEntries[iIndex], sPath + oItem['name'] + '/', fCallback, fLimitCallbackProxy);
247
									}
248
								}
249
							});
250
						}
251
					}
252
				}
253
			}
254
		;
255
	
256
		bAllowFolderDragAndDrop = isUndefined(bAllowFolderDragAndDrop) ? true : !!bAllowFolderDragAndDrop;
257
	
258
		bEntry = isUndefined(bEntry) ? false : !!bEntry;
259
		iLimit = isUndefined(iLimit) ? Jua.iDefLimit : pInt(iLimit);
260
		iInputLimit = iLimit;
261
		bUseLimit = 0 < iLimit;
262
	
263
		aItems = aItems && 0 < aItems.length ? aItems : null;
264
		if (aItems)
265
		{
266
			for (iIndex = 0, iLen = aItems.length; iIndex < iLen; iIndex++)
267
			{
268
				oItem = aItems[iIndex];
269
				if (oItem)
270
				{
271
					if (bEntry)
272
					{
273
						if ('file' === oItem['kind'] && oItem['webkitGetAsEntry'])
274
						{
275
							oEntry = oItem['webkitGetAsEntry']();
276
							if (oEntry)
277
							{
278
								fTraverseFileTree(oEntry, '', fFileCallback, fLimitCallback);
279
							}
280
						}
281
					}
282
					else
283
					{
284
						if (!bUseLimit || 0 <= --iLimit)
285
						{
286
							fFileCallback(getDataFromFile(oItem));
287
						}
288
						else if (bUseLimit && !bCallLimit)
289
						{
290
							if (0 > iLimit && fLimitCallback)
291
							{
292
								bCallLimit = true;
293
								fLimitCallback(iInputLimit);
294
							}
295
						}
296
					}
297
				}
298
			}
299
		}
300
	}
301
	
302
	/**
303
	 * @param {*} oInput
304
	 * @param {Function} fFileCallback
305
	 * @param {number=} iLimit = 20
306
	 * @param {Function=} fLimitCallback
307
	 */
308
	function getDataFromInput(oInput, fFileCallback, iLimit, fLimitCallback)
309
	{
310
		var aFiles = oInput && oInput.files && 0 < oInput.files.length ? oInput.files : null;
311
		if (aFiles)
312
		{
313
			getDataFromFiles(aFiles, fFileCallback, false, false, iLimit, fLimitCallback);
314
		}
315
		else
316
		{
317
			fFileCallback({
318
				'FileName': oInput.value.split('\\').pop().split('/').pop(),
319
				'Size': null,
320
				'Type': null,
321
				'Folder': '',
322
				'File' : null
323
			});
324
		}
325
	}
326
	
327
	function eventContainsFiles(oEvent)
328
	{
329
		var bResult = false;
330
		if (oEvent && oEvent.dataTransfer && oEvent.dataTransfer.types && oEvent.dataTransfer.types.length)
331
		{
332
			var
333
				iIindex = 0,
334
				iLen = oEvent.dataTransfer.types.length
335
			;
336
	
337
			for (; iIindex < iLen; iIindex++)
338
			{
339
				if (oEvent.dataTransfer.types[iIindex].toLowerCase() === 'files')
340
				{
341
					bResult = true;
342
					break;
343
				}
344
			}
345
		}
346
	
347
		return bResult;
348
	}
349
	
350
	/**
351
	 * @param {Event} oEvent
352
	 * @param {Function} fFileCallback
353
	 * @param {number=} iLimit = 20
354
	 * @param {Function=} fLimitCallback
355
	 * @param {boolean=} bAllowFolderDragAndDrop = true
356
	 */
357
	function getDataFromDragEvent(oEvent, fFileCallback, iLimit, fLimitCallback, bAllowFolderDragAndDrop)
358
	{
359
		var
360
			aItems = null,
361
			aFiles = null
362
		;
363
	
364
		oEvent = getEvent(oEvent);
365
		if (oEvent)
366
		{
367
			aItems = (oEvent.dataTransfer ? getValue(oEvent.dataTransfer, 'items', null) : null) || getValue(oEvent, 'items', null);
368
			if (aItems && 0 < aItems.length && aItems[0] && aItems[0]['webkitGetAsEntry'])
369
			{
370
				getDataFromFiles(aItems, fFileCallback, true, bAllowFolderDragAndDrop, iLimit, fLimitCallback);
371
			}
372
			else if (eventContainsFiles(oEvent))
373
			{
374
				aFiles = (getValue(oEvent, 'files', null) || (oEvent.dataTransfer ?
375
					getValue(oEvent.dataTransfer, 'files', null) : null));
376
	
377
				if (aFiles && 0 < aFiles.length)
378
				{
379
					getDataFromFiles(aFiles, fFileCallback, false, false, iLimit, fLimitCallback);
380
				}
381
			}
382
		}
383
	}
384
	
385
	function createNextLabel()
386
	{
387
		return $('<label style="' +
388
	'position: absolute; background-color:#fff; right: 0px; top: 0px; left: 0px; bottom: 0px; margin: 0px; padding: 0px; cursor: pointer;' +
389
		'"></label>').css({
390
			'opacity': 0
391
		});
392
	}
393
	
394
	function createNextInput()
395
	{
396
		return $('<input type="file" tabindex="-1" hidefocus="hidefocus" style="position: absolute; left: -9999px;" />');
397
	}
398
	
399
	/**
400
	 * @param {string=} sName
401
	 * @param {boolean=} bMultiple = true
402
	 * @return {?Object}
403
	 */
404
	function getNewInput(sName, bMultiple)
405
	{
406
		sName = isUndefined(sName) ? '' : sName.toString();
407
	
408
		var oLocal = createNextInput();
409
		if (0 < sName.length)
410
		{
411
			oLocal.attr('name', sName);
412
		}
413
	
414
		if (isUndefined(bMultiple) ? true : bMultiple)
415
		{
416
			oLocal.prop('multiple', true);
417
		}
418
	
419
		return oLocal;
420
	}
421
	
422
	/**
423
	 * @param {?} mStringOrFunction
424
	 * @param {Array=} aFunctionParams
425
	 * @return {string}
426
	 */
427
	function getStringOrCallFunction(mStringOrFunction, aFunctionParams)
428
	{
429
		return $.isFunction(mStringOrFunction) ? 
430
			mStringOrFunction.apply(null, $.isArray(aFunctionParams) ? aFunctionParams : []).toString() :
431
			mStringOrFunction.toString();
432
	}
433
	
434
	/**
435
	 * @constructor
436
	 * @param {Jua} oJua
437
	 * @param {Object} oOptions
438
	 */
439
	function AjaxDriver(oJua, oOptions)
440
	{
441
		this.oXhrs = {};
442
		this.oUids = {};
443
		this.oJua = oJua;
444
		this.oOptions = oOptions;
445
	}
446
	
447
	/**
448
	 * @type {Object}
449
	 */
450
	AjaxDriver.prototype.oXhrs = {};
451
	
452
	/**
453
	 * @type {Object}
454
	 */
455
	AjaxDriver.prototype.oUids = {};
456
	
457
	/**
458
	 * @type {?Jua}
459
	 */
460
	AjaxDriver.prototype.oJua = null;
461
	
462
	/**
463
	 * @type {Object}
464
	 */
465
	AjaxDriver.prototype.oOptions = {};
466
	
467
	/**
468
	 * @return {boolean}
469
	 */
470
	AjaxDriver.prototype.isDragAndDropSupported = function ()
471
	{
472
		return true;
473
	};
474
	
475
	/**
476
	 * @param {string} sUid
477
	 */
478
	AjaxDriver.prototype.regTaskUid = function (sUid)
479
	{
480
		this.oUids[sUid] = true;
481
	};
482
	
483
	/**
484
	 * @param {string} sUid
485
	 * @param {?} oFileInfo
486
	 * @param {Function} fCallback
487
	 */
488
	AjaxDriver.prototype.uploadTask = function (sUid, oFileInfo, fCallback)
489
	{
490
		if (false === this.oUids[sUid] || !oFileInfo || !oFileInfo['File'])
491
		{
492
			fCallback(null, sUid);
493
			return false;
494
		}
495
	
496
		try
497
		{
498
			var
499
				self = this,
500
				oXhr = new XMLHttpRequest(),
501
				oFormData = new FormData(),
502
				sAction = getValue(this.oOptions, 'action', ''),
503
				aHidden = getValue(this.oOptions, 'hidden', {}),
504
				fStartFunction = this.oJua.getEvent('onStart'),
505
				fCompleteFunction = this.oJua.getEvent('onComplete'),
506
				fProgressFunction = this.oJua.getEvent('onProgress')
507
			;
508
	
509
			oXhr.open('POST', sAction, true);
510
	
511
			if (fProgressFunction && oXhr.upload)
512
			{
513
				oXhr.upload.onprogress = function (oEvent) {
514
					if (oEvent && oEvent.lengthComputable && !isUndefined(oEvent.loaded) && !isUndefined(oEvent.total))
515
					{
516
						fProgressFunction(sUid, oEvent.loaded, oEvent.total);
517
					}
518
				};
519
			}
520
	
521
			oXhr.onreadystatechange = function () {
522
				if (4 === oXhr.readyState && 200 === oXhr.status)
523
				{
524
					if (fCompleteFunction)
525
					{
526
						var
527
							bResult = false,
528
							oResult = null
529
						;
530
	
531
						try
532
						{
533
							oResult = $.parseJSON(oXhr.responseText);
534
							bResult = true;
535
						}
536
						catch (oException)
537
						{
538
							oResult = null;
539
						}
540
	
541
						fCompleteFunction(sUid, bResult, oResult);
542
					}
543
	
544
					if (!isUndefined(self.oXhrs[sUid]))
545
					{
546
						self.oXhrs[sUid] = null;
547
					}
548
	
549
					fCallback(null, sUid);
550
				}
551
				else
552
				{
553
					if (4 === oXhr.readyState)
554
					{
555
						fCompleteFunction(sUid, false, null);
556
						fCallback(null, sUid);
557
					}
558
				}
559
			};
560
	
561
			if (fStartFunction)
562
			{
563
				fStartFunction(sUid);
564
			}
565
	
566
			oFormData.append('jua-post-type', 'ajax');
567
			oFormData.append(getValue(this.oOptions, 'name', 'juaFile'), oFileInfo['File']);
568
			$.each(aHidden, function (sKey, sValue) {
569
				oFormData.append(sKey, getStringOrCallFunction(sValue, [oFileInfo]));
570
			});
571
	
572
			oXhr.send(oFormData);
573
	
574
			this.oXhrs[sUid] = oXhr;
575
			return true;
576
		}
577
		catch (oError)
578
		{
579
		}
580
	
581
		fCallback(null, sUid);
582
		return false;
583
	};
584
	
585
	AjaxDriver.prototype.generateNewInput = function (oClickElement)
586
	{
587
		var
588
			self = this,
589
			oLabel = null,
590
			oInput = null
591
		;
592
	
593
		if (oClickElement)
594
		{
595
			oInput = getNewInput('', !getValue(this.oOptions, 'disableMultiple', false));
596
			oLabel = createNextLabel();
597
			oLabel.append(oInput);
598
	
599
			$(oClickElement).append(oLabel);
600
	
601
			oInput
602
				.on('click', function () {
603
					var fOn = self.oJua.getEvent('onDialog');
604
					if (fOn)
605
					{
606
						fOn();
607
					}
608
				})
609
				.on('change', function () {
610
					getDataFromInput(this, function (oFile) {
611
							self.oJua.addNewFile(oFile);
612
							self.generateNewInput(oClickElement);
613
	
614
							setTimeout(function () {
615
								oLabel.remove();
616
							}, 10);
617
						},
618
						getValue(self.oOptions, 'multipleSizeLimit', Jua.iDefLimit),
619
						self.oJua.getEvent('onLimitReached')
620
					);
621
				})
622
			;
623
		}
624
	};
625
	
626
	AjaxDriver.prototype.cancel = function (sUid)
627
	{
628
		this.oUids[sUid] = false;
629
		if (this.oXhrs[sUid])
630
		{
631
			try
632
			{
633
				if (this.oXhrs[sUid].abort)
634
				{
635
					this.oXhrs[sUid].abort();
636
				}
637
			}
638
			catch (oError)
639
			{
640
			}
641
	
642
			this.oXhrs[sUid] = null;
643
		}
644
	};
645
	
646
	/**
647
	 * @constructor
648
	 * @param {Jua} oJua
649
	 * @param {Object} oOptions
650
	 */
651
	function IframeDriver(oJua, oOptions)
652
	{
653
		this.oUids = {};
654
		this.oForms = {};
655
		this.oJua = oJua;
656
		this.oOptions = oOptions;
657
	}
658
	
659
	/**
660
	 * @type {Object}
661
	 */
662
	IframeDriver.prototype.oUids = {};
663
	
664
	/**
665
	 * @type {Object}
666
	 */
667
	IframeDriver.prototype.oForms = {};
668
	
669
	/**
670
	 * @type {?Jua}
671
	 */
672
	IframeDriver.prototype.oJua = null;
673
	
674
	/**
675
	 * @type {Object}
676
	 */
677
	IframeDriver.prototype.oOptions = {};
678
	
679
	/**
680
	 * @return {boolean}
681
	 */
682
	IframeDriver.prototype.isDragAndDropSupported = function ()
683
	{
684
		return false;
685
	};
686
	
687
	/**
688
	 * @param {string} sUid
689
	 */
690
	IframeDriver.prototype.regTaskUid = function (sUid)
691
	{
692
		this.oUids[sUid] = true;
693
	};
694
	
695
	/**
696
	 * @param {string} sUid
697
	 * @param {?} oFileInfo
698
	 * @param {Function} fCallback
699
	 */
700
	IframeDriver.prototype.uploadTask = function (sUid, oFileInfo, fCallback)
701
	{
702
		if (false === this.oUids[sUid])
703
		{
704
			fCallback(null, sUid);
705
			return false;
706
		}
707
	
708
		var
709
			oForm = this.oForms[sUid],
710
			aHidden = getValue(this.oOptions, 'hidden', {}),
711
			fStartFunction = this.oJua.getEvent('onStart'),
712
			fCompleteFunction = this.oJua.getEvent('onComplete')
713
		;
714
	
715
		if (oForm)
716
		{
717
			oForm.append($('<input type="hidden" />').attr('name', 'jua-post-type').val('iframe'));
718
			$.each(aHidden, function (sKey, sValue) {
719
				oForm.append($('<input type="hidden" />').attr('name', sKey).val(getStringOrCallFunction(sValue, [oFileInfo])));
720
			});
721
	
722
			oForm.trigger('submit');
723
			if (fStartFunction)
724
			{
725
				fStartFunction(sUid);
726
			}
727
	
728
			oForm.find('iframe').on('load', function (oEvent) {
729
	
730
				var
731
					bResult = false,
732
					oIframeDoc = null,
733
					oResult = {}
734
				;
735
	
736
				if (fCompleteFunction)
737
				{
738
					try
739
					{
740
						oIframeDoc = this.contentDocument ? this.contentDocument: this.contentWindow.document;
741
						oResult = $.parseJSON(oIframeDoc.body.innerHTML);
742
						bResult = true;
743
					}
744
					catch (oErr)
745
					{
746
						oResult = {};
747
					}
748
	
749
					fCompleteFunction(sUid, bResult, oResult);
750
				}
751
	
752
				fCallback(null, sUid);
753
	
754
				window.setTimeout(function () {
755
					oForm.remove();
756
				}, 100);
757
			});
758
		}
759
		else
760
		{
761
			fCallback(null, sUid);
762
		}
763
	
764
		return true;
765
	};
766
	
767
	IframeDriver.prototype.generateNewInput = function (oClickElement)
768
	{
769
		var
770
			self = this,
771
			sUid = '',
772
			oInput = null,
773
			oIframe = null,
774
			sAction = getValue(this.oOptions, 'action', ''),
775
			oForm = null
776
		;
777
	
778
		if (oClickElement)
779
		{
780
			sUid = getNewUid();
781
			
782
			oInput = getNewInput(getValue(this.oOptions, 'name', 'juaFile'), !getValue(this.oOptions, 'disableMultiple', false));
783
	
784
			oForm = $('<form action="' + sAction + '" target="iframe-' + sUid + '" ' +
785
	' method="POST" enctype="multipart/form-data" style="display: block; cursor: pointer;"></form>');
786
	
787
			oIframe = $('<iframe name="iframe-' + sUid + '" tabindex="-1" src="javascript:void(0);" ' +
788
	' style="position: absolute; top: -1000px; left: -1000px; cursor: pointer;" />').css({'opacity': 0});
789
	
790
			oForm.append(createNextLabel().append(oInput)).append(oIframe);
791
	
792
			$(oClickElement).append(oForm);
793
			
794
			this.oForms[sUid] = oForm;
795
	
796
			oInput
797
				.on('click', function () {
798
					var fOn = self.oJua.getEvent('onDialog');
799
					if (fOn)
800
					{
801
						fOn();
802
					}
803
				})
804
				.on('change', function () {
805
					getDataFromInput(this, function (oFile) {
806
							if (oFile)
807
							{
808
								oForm.css({
809
									'position': 'absolute',
810
									'top': -1000,
811
									'left': -1000
812
								});
813
	
814
								self.oJua.addFile(sUid, oFile);
815
								self.generateNewInput(oClickElement);
816
							}
817
	
818
						},
819
						getValue(self.oOptions, 'multipleSizeLimit', Jua.iDefLimit),
820
						self.oJua.getEvent('onLimitReached')
821
					);
822
				})
823
			;
824
		}
825
	};
826
	
827
	IframeDriver.prototype.cancel = function (sUid)
828
	{
829
		this.oUids[sUid] = false;
830
		if (this.oForms[sUid])
831
		{
832
			this.oForms[sUid].remove();
833
			this.oForms[sUid] = false;
834
		}
835
	};
836
	
837
	/**
838
	 * @constructor
839
	 * @param {Object=} oOptions
840
	 */
841
	function Jua(oOptions)
842
	{
843
		oOptions = isUndefined(oOptions) ? {} : oOptions;
844
	
845
		var self = this;
846
	
847
		self.bEnableDnD = true;
848
		
849
		self.oEvents = {
850
			'onDialog': null,
851
			'onSelect': null,
852
			'onStart': null,
853
			'onComplete': null,
854
			'onCompleteAll': null,
855
			'onProgress': null,
856
			'onDragEnter': null,
857
			'onDragLeave': null,
858
			'onDrop': null,
859
			'onBodyDragEnter': null,
860
			'onBodyDragLeave': null,
861
			'onLimitReached': null
862
		};
863
		
864
		self.oOptions = extend({
865
			'action': '',
866
			'name': '',
867
			'hidden': {},
868
			'queueSize': 10,
869
			'clickElement': false,
870
			'dragAndDropElement': false,
871
			'dragAndDropBodyElement': false,
872
			'disableAjaxUpload': false,
873
			'disableFolderDragAndDrop': true,
874
			'disableDragAndDrop': false,
875
			'disableMultiple': false,
876
			'disableDocumentDropPrevent': false,
877
			'multipleSizeLimit': 50
878
		}, oOptions);
879
	
880
		self.oQueue = queue(pInt(getValue(self.oOptions, 'queueSize', 10)));
881
		if (self.runEvent('onCompleteAll'))
882
		{
883
			self.oQueue.await(function () {
884
				self.runEvent('onCompleteAll');
885
			});
886
		}
887
	
888
		self.oDriver = self.isAjaxUploaderSupported() && !getValue(self.oOptions, 'disableAjaxUpload', false) ?
889
			new AjaxDriver(self, self.oOptions) : new IframeDriver(self, self.oOptions);
890
	
891
		self.oClickElement = getValue(self.oOptions, 'clickElement', null);
892
	
893
		if (self.oClickElement)
894
		{
895
			$(self.oClickElement).css({
896
				'position': 'relative',
897
				'overflow': 'hidden'
898
			});
899
	
900
			if ('inline' === $(this.oClickElement).css('display'))
901
			{
902
				$(this.oClickElement).css('display', 'inline-block');
903
			}
904
	
905
			this.oDriver.generateNewInput(this.oClickElement);
906
		}
907
	
908
		if (this.oDriver.isDragAndDropSupported() && getValue(this.oOptions, 'dragAndDropElement', false) &&
909
			!getValue(this.oOptions, 'disableAjaxUpload', false))
910
		{
911
			(function (self) {
912
				var
913
					$doc = $(document),
914
					oBigDropZone = $(getValue(self.oOptions, 'dragAndDropBodyElement', false) || $doc),
915
					oDragAndDropElement = getValue(self.oOptions, 'dragAndDropElement', false),
916
					fHandleDragOver = function (oEvent) {
917
						if (self.bEnableDnD && oEvent)
918
						{
919
							oEvent = getEvent(oEvent);
920
							if (oEvent && eventContainsFiles(oEvent))
921
							{
922
								mainClearTimeout(self.iDocTimer);
923
	
924
								var sEffect = oEvent.dataTransfer.effectAllowed;
925
								oEvent.dataTransfer.dropEffect = (sEffect === 'move' || sEffect === 'linkMove') ? 'move' : 'copy';
926
	
927
								oEvent.stopPropagation();
928
								oEvent.preventDefault();
929
	
930
								oBigDropZone.trigger('dragover', oEvent);
931
							}
932
						}
933
					},
934
					fHandleDrop = function (oEvent) {
935
						if (self.bEnableDnD && oEvent)
936
						{
937
							oEvent = getEvent(oEvent);
938
							if (oEvent && eventContainsFiles(oEvent))
939
							{
940
								oEvent.preventDefault();
941
	
942
								getDataFromDragEvent(oEvent, function (oFile) {
943
										if (oFile)
944
										{
945
											self.runEvent('onDrop', [oFile, oEvent]);
946
											self.addNewFile(oFile);
947
											mainClearTimeout(self.iDocTimer);
948
										}
949
									},
950
									getValue(self.oOptions, 'multipleSizeLimit', Jua.iDefLimit),
951
									self.getEvent('onLimitReached'),
952
									!getValue(self.oOptions, 'disableFolderDragAndDrop', true)
953
								);
954
							}
955
						}
956
	
957
						self.runEvent('onDragLeave', [oEvent]);
958
					},
959
					fHandleDragEnter = function (oEvent) {
960
						if (self.bEnableDnD && oEvent)
961
						{
962
							oEvent = getEvent(oEvent);
963
							if (oEvent && eventContainsFiles(oEvent))
964
							{
965
								mainClearTimeout(self.iDocTimer);
966
	
967
								oEvent.preventDefault();
968
								self.runEvent('onDragEnter', [oDragAndDropElement, oEvent]);
969
							}
970
						}
971
					},
972
					fHandleDragLeave = function (oEvent) {
973
						if (self.bEnableDnD && oEvent)
974
						{
975
							oEvent = getEvent(oEvent);
976
							if (oEvent)
977
							{
978
								var oRelatedTarget = document['elementFromPoint'] ? document['elementFromPoint'](oEvent['clientX'], oEvent['clientY']) : null;
979
								if (oRelatedTarget && contains(this, oRelatedTarget))
980
								{
981
									return;
982
								}
983
	
984
								mainClearTimeout(self.iDocTimer);
985
								self.runEvent('onDragLeave', [oDragAndDropElement, oEvent]);
986
							}
987
	
988
							return;
989
						}
990
					}
991
				;
992
	
993
				if (oDragAndDropElement)
994
				{
995
					if (!getValue(self.oOptions, 'disableDocumentDropPrevent', false))
996
					{
997
						$doc.on('dragover', function (oEvent) {
998
							if (self.bEnableDnD && oEvent)
999
							{
1000
								oEvent = getEvent(oEvent);
1001
								if (oEvent && eventContainsFiles(oEvent))
1002
								{
1003
									oEvent.dataTransfer.dropEffect = 'none';
1004
									oEvent.preventDefault();
1005
								}
1006
							}
1007
						});
1008
					}
1009
	
1010
					if (oBigDropZone && oBigDropZone[0])
1011
					{
1012
						oBigDropZone
1013
							.on('dragover', function (oEvent) {
1014
								if (self.bEnableDnD && oEvent)
1015
								{
1016
									mainClearTimeout(self.iDocTimer);
1017
								}
1018
							})
1019
							.on('dragenter', function (oEvent) {
1020
								if (self.bEnableDnD && oEvent)
1021
								{
1022
									oEvent = getEvent(oEvent);
1023
									if (oEvent && eventContainsFiles(oEvent))
1024
									{
1025
										mainClearTimeout(self.iDocTimer);
1026
										oEvent.preventDefault();
1027
	
1028
										self.runEvent('onBodyDragEnter', [oEvent]);
1029
									}
1030
								}
1031
							})
1032
							.on('dragleave', function (oEvent) {
1033
								if (self.bEnableDnD && oEvent)
1034
								{
1035
									oEvent = getEvent(oEvent);
1036
									if (oEvent)
1037
									{
1038
										mainClearTimeout(self.iDocTimer);
1039
										self.iDocTimer = setTimeout(function () {
1040
											self.runEvent('onBodyDragLeave', [oEvent]);
1041
										}, 200);
1042
									}
1043
								}
1044
							})
1045
							.on('drop', function (oEvent) {
1046
								if (self.bEnableDnD && oEvent)
1047
								{
1048
									oEvent = getEvent(oEvent);
1049
									if (oEvent)
1050
									{
1051
										var bFiles = eventContainsFiles(oEvent);
1052
										if (bFiles)
1053
										{
1054
											oEvent.preventDefault();
1055
										}
1056
	
1057
										self.runEvent('onBodyDragLeave', [oEvent]);
1058
										
1059
										return !bFiles;
1060
									}
1061
								}
1062
	
1063
								return false;
1064
							})
1065
						;
1066
					}
1067
	
1068
					$(oDragAndDropElement)
1069
						.bind('dragenter', fHandleDragEnter)
1070
						.bind('dragover', fHandleDragOver)
1071
						.bind('dragleave', fHandleDragLeave)
1072
						.bind('drop', fHandleDrop)
1073
					;
1074
				}
1075
	
1076
			}(self));
1077
		}
1078
		else
1079
		{
1080
			self.bEnableDnD = false;
1081
		}
1082
	
1083
		setValue(self, 'on', self.on);
1084
		setValue(self, 'cancel', self.cancel);
1085
		setValue(self, 'isDragAndDropSupported', self.isDragAndDropSupported);
1086
		setValue(self, 'isAjaxUploaderSupported', self.isAjaxUploaderSupported);
1087
		setValue(self, 'setDragAndDropEnabledStatus', self.setDragAndDropEnabledStatus);
1088
	}
1089
	
1090
	/**
1091
	 * @type {Function}
1092
	 */
1093
	Jua.fEmptyFunction = function () {};
1094
	
1095
	/**
1096
	 * @type {number}
1097
	 */
1098
	Jua.iDefLimit = 20;
1099
	
1100
	/**
1101
	 * @type {boolean}
1102
	 */
1103
	Jua.isAjaxUploaderSupported = (function () {
1104
		var oInput = document.createElement('input');
1105
		oInput.type = 'file';
1106
		return !!('XMLHttpRequest' in window && 'multiple' in oInput && 'FormData' in window && (new XMLHttpRequest()).upload && true);
1107
	}());
1108
	
1109
	/**
1110
	 * @type {boolean}
1111
	 */
1112
	Jua.prototype.bEnableDnD = true;
1113
	
1114
	/**
1115
	 * @type {number}
1116
	 */
1117
	Jua.prototype.iDocTimer = 0;
1118
	
1119
	/**
1120
	 * @type {Object}
1121
	 */
1122
	Jua.prototype.oOptions = {};
1123
	
1124
	/**
1125
	 * @type {Object}
1126
	 */
1127
	Jua.prototype.oEvents = {};
1128
	
1129
	/**
1130
	 * @type {?Object}
1131
	 */
1132
	Jua.prototype.oQueue = null;
1133
	
1134
	/**
1135
	 * @type {?Object}
1136
	 */
1137
	Jua.prototype.oDriver = null;
1138
	
1139
	/**
1140
	 * @param {string} sName
1141
	 * @param {Function} fFunc
1142
	 */
1143
	Jua.prototype.on = function (sName, fFunc)
1144
	{
1145
		this.oEvents[sName] = fFunc;
1146
		return this;
1147
	};
1148
	
1149
	/**
1150
	 * @param {string} sName
1151
	 * @param {string=} aArgs
1152
	 */
1153
	Jua.prototype.runEvent = function (sName, aArgs)
1154
	{
1155
		if (this.oEvents[sName])
1156
		{
1157
			this.oEvents[sName].apply(null, aArgs || []);
1158
		}
1159
	};
1160
	
1161
	/**
1162
	 * @param {string} sName
1163
	 */
1164
	Jua.prototype.getEvent = function (sName)
1165
	{
1166
		return this.oEvents[sName] || null;
1167
	};
1168
	
1169
	/**
1170
	 * @param {string} sUid
1171
	 */
1172
	Jua.prototype.cancel = function (sUid)
1173
	{
1174
		this.oDriver.cancel(sUid);
1175
	};
1176
	
1177
	/**
1178
	 * @return {boolean}
1179
	 */
1180
	Jua.prototype.isAjaxUploaderSupported = function ()
1181
	{
1182
		return Jua.isAjaxUploaderSupported;
1183
	};
1184
	
1185
	/**
1186
	 * @param {boolean} bEnabled
1187
	 */
1188
	Jua.prototype.setDragAndDropEnabledStatus = function (bEnabled)
1189
	{
1190
		this.bEnableDnD = !!bEnabled;
1191
	};
1192
	
1193
	/**
1194
	 * @return {boolean}
1195
	 */
1196
	Jua.prototype.isDragAndDropSupported = function ()
1197
	{
1198
		return this.oDriver.isDragAndDropSupported();
1199
	};
1200
	
1201
	/**
1202
	 * @param {Object} oFileInfo
1203
	 */
1204
	Jua.prototype.addNewFile = function (oFileInfo)
1205
	{
1206
		this.addFile(getNewUid(), oFileInfo);
1207
	};
1208
	
1209
	/**
1210
	 * @param {string} sUid
1211
	 * @param {Object} oFileInfo
1212
	 */
1213
	Jua.prototype.addFile = function (sUid, oFileInfo)
1214
	{
1215
		var fOnSelect = this.getEvent('onSelect');
1216
		if (oFileInfo && (!fOnSelect || (false !== fOnSelect(sUid, oFileInfo))))
1217
		{
1218
			this.oDriver.regTaskUid(sUid);
1219
			this.oQueue.defer(scopeBind(this.oDriver.uploadTask, this.oDriver), sUid, oFileInfo);
1220
		}
1221
		else
1222
		{
1223
			this.oDriver.cancel(sUid);
1224
		}
1225
	};
1226
	
1227
	/**
1228
	 * @type {Jua}
1229
	 */
1230
	window['Jua'] = Jua;
1231
	
1232
}(jQuery, window, queue));