h5_uploader 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,14 +1,23 @@
1
- Set up
2
- ====
1
+ ##Install
2
+
3
+ gem install h5_uploader
4
+
3
5
 
4
- config/application.rb
5
- ----
6
+ ##Set up
7
+ Run the install task that copy the necessary js and css resources.
6
8
 
7
- Add requre after the rails require statement
9
+ rake h5_uploader:install
8
10
 
9
- __require "rack-raw-upload"__
11
+ In config/application.rb add the following settings:
10
12
 
11
- Add the middleware config
13
+ require "rack-raw-upload"
14
+
15
+ and
12
16
 
13
- __config.middleware.use 'Rack::RawUpload'__
17
+ config.middleware.use 'Rack::RawUpload'
18
+
19
+ Run bundle install.
14
20
 
21
+
22
+ ##js library options and docs
23
+ See [file-upload](https://github.com/jgoguen/file-uploader)
@@ -1,11 +1,21 @@
1
1
  /**
2
2
  * http://github.com/valums/file-uploader
3
- *
4
- * Multiple file upload component with progress-bar, drag-and-drop.
5
- * © 2010 Andrew Valums ( andrew(at)valums.com )
6
- *
3
+ *
4
+ * Multiple file upload component with progress-bar, drag-and-drop.
5
+ * © 2010 Andrew Valums ( andrew(at)valums.com )
6
+ *
7
+ * Licensed under GNU GPL 2 or later, see license.txt.
8
+ */
9
+ /**
10
+ * http://github.com/jgoguen/file-uploader
11
+ * Forked from http://github.com/valums/file-uploader
12
+ * © 2010 Andrew Valums ( andrew(at)valums.com )
13
+ * © 2011 Joel Goguen ( jgoguen(at)jgoguen.ca )
14
+ *
15
+ * Script now depends on jQuery for functioning.
16
+ *
7
17
  * Licensed under GNU GPL 2 or later and GNU LGPL 2 or later, see license.txt.
8
- */
18
+ */
9
19
 
10
20
  //
11
21
  // Helper functions
@@ -15,12 +25,12 @@ var qq = qq || {};
15
25
 
16
26
  /**
17
27
  * Adds all missing properties from second obj to first obj
18
- */
28
+ */
19
29
  qq.extend = function(first, second){
20
30
  for (var prop in second){
21
31
  first[prop] = second[prop];
22
32
  }
23
- };
33
+ };
24
34
 
25
35
  /**
26
36
  * Searches for a given element in the array, returns -1 if it is not present.
@@ -28,20 +38,20 @@ qq.extend = function(first, second){
28
38
  */
29
39
  qq.indexOf = function(arr, elt, from){
30
40
  if (arr.indexOf) return arr.indexOf(elt, from);
31
-
41
+
32
42
  from = from || 0;
33
- var len = arr.length;
34
-
35
- if (from < 0) from += len;
43
+ var len = arr.length;
44
+
45
+ if (from < 0) from += len;
36
46
 
37
- for (; from < len; from++){
38
- if (from in arr && arr[from] === elt){
47
+ for (; from < len; from++){
48
+ if (from in arr && arr[from] === elt){
39
49
  return from;
40
50
  }
41
- }
42
- return -1;
43
- };
44
-
51
+ }
52
+ return -1;
53
+ };
54
+
45
55
  qq.getUniqueId = (function(){
46
56
  var id = 0;
47
57
  return function(){ return id++; };
@@ -86,10 +96,10 @@ qq.remove = function(element){
86
96
  element.parentNode.removeChild(element);
87
97
  };
88
98
 
89
- qq.contains = function(parent, descendant){
99
+ qq.contains = function(parent, descendant){
90
100
  // compareposition returns false in this case
91
101
  if (parent == descendant) return true;
92
-
102
+
93
103
  if (parent.contains){
94
104
  return parent.contains(descendant);
95
105
  } else {
@@ -181,7 +191,7 @@ qq.getByClass = function(element, className){
181
191
  /**
182
192
  * obj2url() takes a json-object as argument and generates
183
193
  * a querystring. pretty much like jQuery.param()
184
- *
194
+ *
185
195
  * how to use:
186
196
  *
187
197
  * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');`
@@ -198,21 +208,21 @@ qq.obj2url = function(obj, temp, prefixDone){
198
208
  var uristrings = [],
199
209
  prefix = '&',
200
210
  add = function(nextObj, i){
201
- var nextTemp = temp
211
+ var nextTemp = temp
202
212
  ? (/\[\]$/.test(temp)) // prevent double-encoding
203
213
  ? temp
204
214
  : temp+'['+i+']'
205
215
  : i;
206
- if ((nextTemp != 'undefined') && (i != 'undefined')) {
216
+ if ((nextTemp != 'undefined') && (i != 'undefined')) {
207
217
  uristrings.push(
208
- (typeof nextObj === 'object')
218
+ (typeof nextObj === 'object')
209
219
  ? qq.obj2url(nextObj, nextTemp, true)
210
220
  : (Object.prototype.toString.call(nextObj) === '[object Function]')
211
221
  ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj())
212
- : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)
222
+ : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)
213
223
  );
214
224
  }
215
- };
225
+ };
216
226
 
217
227
  if (!prefixDone && temp) {
218
228
  prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?';
@@ -234,7 +244,7 @@ qq.obj2url = function(obj, temp, prefixDone){
234
244
 
235
245
  return uristrings.join(prefix)
236
246
  .replace(/^&/, '')
237
- .replace(/%20/g, '+');
247
+ .replace(/%20/g, '+');
238
248
  };
239
249
 
240
250
  //
@@ -244,9 +254,9 @@ qq.obj2url = function(obj, temp, prefixDone){
244
254
  //
245
255
 
246
256
  var qq = qq || {};
247
-
257
+
248
258
  /**
249
- * Creates upload button, validates upload, but doesn't create file list or dd.
259
+ * Creates upload button, validates upload, but doesn't create file list or dd.
250
260
  */
251
261
  qq.FileUploaderBasic = function(o){
252
262
  this._options = {
@@ -255,221 +265,243 @@ qq.FileUploaderBasic = function(o){
255
265
  action: '/server/upload',
256
266
  params: {},
257
267
  button: null,
268
+ uploadButtonText: 'Upload a file',
258
269
  multiple: true,
259
270
  maxConnections: 3,
260
- // validation
261
- allowedExtensions: [],
262
- sizeLimit: 0,
263
- minSizeLimit: 0,
271
+ useBase2Prefixes: false,
272
+ // validation
273
+ allowedExtensions: [],
274
+ sizeLimit: 0,
275
+ minSizeLimit: 0,
264
276
  // events
265
277
  // return false to cancel submit
266
278
  onSubmit: function(id, fileName){},
267
279
  onProgress: function(id, fileName, loaded, total){},
268
280
  onComplete: function(id, fileName, responseJSON){},
269
281
  onCancel: function(id, fileName){},
270
- // messages
282
+ // messages
271
283
  messages: {
272
284
  typeError: "{file} has invalid extension. Only {extensions} are allowed.",
273
285
  sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
274
286
  minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.",
275
287
  emptyError: "{file} is empty, please select files again without it.",
276
- onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
288
+ onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
277
289
  },
278
290
  showMessage: function(message){
279
291
  alert(message);
280
- }
292
+ }
281
293
  };
282
294
  qq.extend(this._options, o);
283
-
295
+
284
296
  // number of files being uploaded
285
297
  this._filesInProgress = 0;
286
- this._handler = this._createUploadHandler();
287
-
288
- if (this._options.button){
298
+ this._handler = this._createUploadHandler();
299
+
300
+ if (this._options.button){
289
301
  this._button = this._createUploadButton(this._options.button);
290
302
  }
291
-
292
- this._preventLeaveInProgress();
303
+
304
+ this._preventLeaveInProgress();
293
305
  };
294
-
306
+
295
307
  qq.FileUploaderBasic.prototype = {
296
308
  setParams: function(params){
297
309
  this._options.params = params;
298
310
  },
299
311
  getInProgress: function(){
300
- return this._filesInProgress;
312
+ return this._filesInProgress;
301
313
  },
302
314
  _createUploadButton: function(element){
303
315
  var self = this;
304
-
305
- return new qq.UploadButton({
316
+
317
+ var btn = new qq.UploadButton({
306
318
  element: element,
307
319
  multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(),
308
320
  onChange: function(input){
309
321
  self._onInputChange(input);
310
- }
311
- });
312
- },
322
+ }
323
+ });
324
+ $(btn._element).contents().first().replaceWith(this._options.uploadButtonText);
325
+ return btn;
326
+ },
313
327
  _createUploadHandler: function(){
314
328
  var self = this,
315
- handlerClass;
316
-
317
- if(qq.UploadHandlerXhr.isSupported()){
318
- handlerClass = 'UploadHandlerXhr';
329
+ handlerClass;
330
+
331
+ if(qq.UploadHandlerXhr.isSupported()){
332
+ handlerClass = 'UploadHandlerXhr';
319
333
  } else {
320
334
  handlerClass = 'UploadHandlerForm';
321
335
  }
322
336
 
323
337
  var handler = new qq[handlerClass]({
324
338
  debug: this._options.debug,
325
- action: this._options.action,
326
- maxConnections: this._options.maxConnections,
327
- onProgress: function(id, fileName, loaded, total){
339
+ action: this._options.action,
340
+ maxConnections: this._options.maxConnections,
341
+ onProgress: function(id, fileName, loaded, total){
328
342
  self._onProgress(id, fileName, loaded, total);
329
- self._options.onProgress(id, fileName, loaded, total);
330
- },
343
+ self._options.onProgress.call(self, id, fileName, loaded, total);
344
+ },
331
345
  onComplete: function(id, fileName, result){
332
346
  self._onComplete(id, fileName, result);
333
- self._options.onComplete(id, fileName, result);
347
+ self._options.onComplete.call(self, id, fileName, result);
334
348
  },
335
349
  onCancel: function(id, fileName){
336
350
  self._onCancel(id, fileName);
337
- self._options.onCancel(id, fileName);
351
+ self._options.onCancel.call(self, id, fileName);
338
352
  }
339
353
  });
340
354
 
341
355
  return handler;
342
- },
356
+ },
343
357
  _preventLeaveInProgress: function(){
344
358
  var self = this;
345
-
359
+
346
360
  qq.attach(window, 'beforeunload', function(e){
347
361
  if (!self._filesInProgress){return;}
348
-
362
+
349
363
  var e = e || window.event;
350
364
  // for ie, ff
351
365
  e.returnValue = self._options.messages.onLeave;
352
366
  // for webkit
353
- return self._options.messages.onLeave;
354
- });
355
- },
367
+ return self._options.messages.onLeave;
368
+ });
369
+ },
356
370
  _onSubmit: function(id, fileName){
357
- this._filesInProgress++;
371
+ this._filesInProgress++;
358
372
  },
359
- _onProgress: function(id, fileName, loaded, total){
373
+ _onProgress: function(id, fileName, loaded, total){
360
374
  },
361
375
  _onComplete: function(id, fileName, result){
362
- this._filesInProgress--;
376
+ this._filesInProgress--;
363
377
  if (result.error){
364
378
  this._options.showMessage(result.error);
365
- }
379
+ }
366
380
  },
367
381
  _onCancel: function(id, fileName){
368
- this._filesInProgress--;
382
+ this._filesInProgress--;
369
383
  },
370
384
  _onInputChange: function(input){
371
- if (this._handler instanceof qq.UploadHandlerXhr){
372
- this._uploadFileList(input.files);
373
- } else {
374
- if (this._validateFile(input)){
375
- this._uploadFile(input);
376
- }
377
- }
378
- this._button.reset();
379
- },
385
+ if (this._handler instanceof qq.UploadHandlerXhr){
386
+ this._uploadFileList(input.files);
387
+ } else {
388
+ if (this._validateFile(input)){
389
+ this._uploadFile(input);
390
+ }
391
+ }
392
+ this._button.reset();
393
+ },
380
394
  _uploadFileList: function(files){
381
395
  for (var i=0; i<files.length; i++){
382
396
  if ( !this._validateFile(files[i])){
383
397
  return;
384
- }
398
+ }
385
399
  }
386
-
400
+
387
401
  for (var i=0; i<files.length; i++){
388
- this._uploadFile(files[i]);
389
- }
390
- },
391
- _uploadFile: function(fileContainer){
402
+ this._uploadFile(files[i]);
403
+ }
404
+ },
405
+ _uploadFile: function(fileContainer){
392
406
  var id = this._handler.add(fileContainer);
393
407
  var fileName = this._handler.getName(id);
394
-
408
+
395
409
  if (this._options.onSubmit(id, fileName) !== false){
396
410
  this._onSubmit(id, fileName);
397
411
  this._handler.upload(id, this._options.params);
398
412
  }
399
- },
413
+ },
400
414
  _validateFile: function(file){
401
415
  var name, size;
402
-
416
+
403
417
  if (file.value){
404
- // it is a file input
418
+ // it is a file input
405
419
  // get input value and remove path to normalize
406
420
  name = file.value.replace(/.*(\/|\\)/, "");
421
+ if(file.files)
422
+ {
423
+ size = file.files[0].fileSize;
424
+ }
407
425
  } else {
408
426
  // fix missing properties in Safari
409
427
  name = file.fileName != null ? file.fileName : file.name;
410
428
  size = file.fileSize != null ? file.fileSize : file.size;
411
429
  }
412
-
413
- if (! this._isAllowedExtension(name)){
430
+
431
+ if (! this._isAllowedExtension(name)){
414
432
  this._error('typeError', name);
415
433
  return false;
416
-
417
- } else if (size === 0){
434
+
435
+ } else if (size === 0){
418
436
  this._error('emptyError', name);
419
437
  return false;
420
-
421
- } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){
438
+
439
+ } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){
422
440
  this._error('sizeError', name);
423
441
  return false;
424
-
442
+
425
443
  } else if (size && size < this._options.minSizeLimit){
426
444
  this._error('minSizeError', name);
427
- return false;
445
+ return false;
428
446
  }
429
-
430
- return true;
447
+
448
+ return true;
431
449
  },
432
450
  _error: function(code, fileName){
433
- var message = this._options.messages[code];
451
+ var message = this._options.messages[code];
434
452
  function r(name, replacement){ message = message.replace(name, replacement); }
435
-
436
- r('{file}', this._formatFileName(fileName));
453
+
454
+ r('{file}', this._formatFileName(fileName));
437
455
  r('{extensions}', this._options.allowedExtensions.join(', '));
438
456
  r('{sizeLimit}', this._formatSize(this._options.sizeLimit));
439
457
  r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit));
440
-
441
- this._options.showMessage(message);
458
+
459
+ this._options.showMessage(message);
442
460
  },
443
461
  _formatFileName: function(name){
444
462
  if (name.length > 33){
445
- name = name.slice(0, 19) + '...' + name.slice(-13);
463
+ name = name.slice(0, 19) + '...' + name.slice(-13);
446
464
  }
447
465
  return name;
448
466
  },
449
467
  _isAllowedExtension: function(fileName){
450
468
  var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : '';
451
469
  var allowed = this._options.allowedExtensions;
452
-
453
- if (!allowed.length){return true;}
454
-
470
+
471
+ if (!allowed.length){return true;}
472
+
455
473
  for (var i=0; i<allowed.length; i++){
456
- if (allowed[i].toLowerCase() == ext){ return true;}
474
+ if (allowed[i].toLowerCase() == ext){ return true;}
457
475
  }
458
-
476
+
459
477
  return false;
460
- },
478
+ },
461
479
  _formatSize: function(bytes){
462
- var i = -1;
463
- do {
464
- bytes = bytes / 1024;
465
- i++;
466
- } while (bytes > 99);
467
-
468
- return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];
480
+ var i = 0;
481
+ var suffixes;
482
+
483
+ if(this._options.useBase2Prefixes)
484
+ {
485
+ while (bytes > 1024) {
486
+ bytes = bytes / 1024;
487
+ i++;
488
+ }
489
+ suffixes = ['B', 'kiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'YiB'];
490
+ }
491
+ else
492
+ {
493
+ while (bytes > 1000) {
494
+ bytes = bytes / 1000;
495
+ i++;
496
+ }
497
+ suffixes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'YB'];
498
+ }
499
+
500
+ return Math.max(bytes, 0.1).toFixed(1) + suffixes[i];
469
501
  }
470
502
  };
471
-
472
-
503
+
504
+
473
505
  /**
474
506
  * Class that creates upload widget with drag-and-drop and file list
475
507
  * @inherits qq.FileUploaderBasic
@@ -477,17 +509,17 @@ qq.FileUploaderBasic.prototype = {
477
509
  qq.FileUploader = function(o){
478
510
  // call parent constructor
479
511
  qq.FileUploaderBasic.apply(this, arguments);
480
-
481
- // additional options
512
+
513
+ // additional options
482
514
  qq.extend(this._options, {
483
515
  element: null,
484
516
  // if set, will be used instead of qq-upload-list in template
485
517
  listElement: null,
486
-
487
- template: '<div class="qq-uploader">' +
518
+
519
+ template: '<div class="qq-uploader">' +
488
520
  '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
489
521
  '<div class="qq-upload-button">Upload a file</div>' +
490
- '<ul class="qq-upload-list"></ul>' +
522
+ '<ul class="qq-upload-list"></ul>' +
491
523
  '</div>',
492
524
 
493
525
  // template for one item in file list
@@ -497,15 +529,15 @@ qq.FileUploader = function(o){
497
529
  '<span class="qq-upload-size"></span>' +
498
530
  '<a class="qq-upload-cancel" href="#">Cancel</a>' +
499
531
  '<span class="qq-upload-failed-text">Failed</span>' +
500
- '</li>',
501
-
532
+ '</li>',
533
+
502
534
  classes: {
503
535
  // used to get elements from templates
504
536
  button: 'qq-upload-button',
505
537
  drop: 'qq-upload-drop-area',
506
538
  dropActive: 'qq-upload-drop-area-active',
507
539
  list: 'qq-upload-list',
508
-
540
+
509
541
  file: 'qq-upload-file',
510
542
  spinner: 'qq-upload-spinner',
511
543
  size: 'qq-upload-size',
@@ -517,17 +549,17 @@ qq.FileUploader = function(o){
517
549
  fail: 'qq-upload-fail'
518
550
  }
519
551
  });
520
- // overwrite options with user supplied
521
- qq.extend(this._options, o);
552
+ // overwrite options with user supplied
553
+ qq.extend(this._options, o);
522
554
 
523
555
  this._element = this._options.element;
524
- this._element.innerHTML = this._options.template;
556
+ this._element.innerHTML = this._options.template;
525
557
  this._listElement = this._options.listElement || this._find(this._element, 'list');
526
-
558
+
527
559
  this._classes = this._options.classes;
528
-
529
- this._button = this._createUploadButton(this._find(this._element, 'button'));
530
-
560
+
561
+ this._button = this._createUploadButton(this._find(this._element, 'button'));
562
+
531
563
  this._bindCancelEvent();
532
564
  this._setupDragDrop();
533
565
  };
@@ -539,17 +571,17 @@ qq.extend(qq.FileUploader.prototype, {
539
571
  /**
540
572
  * Gets one of the elements listed in this._options.classes
541
573
  **/
542
- _find: function(parent, type){
543
- var element = qq.getByClass(parent, this._options.classes[type])[0];
574
+ _find: function(parent, type){
575
+ var element = qq.getByClass(parent, this._options.classes[type])[0];
544
576
  if (!element){
545
577
  throw new Error('element not found ' + type);
546
578
  }
547
-
579
+
548
580
  return element;
549
581
  },
550
582
  _setupDragDrop: function(){
551
583
  var self = this,
552
- dropArea = this._find(this._element, 'drop');
584
+ dropArea = this._find(this._element, 'drop');
553
585
 
554
586
  var dz = new qq.UploadDropZone({
555
587
  element: dropArea,
@@ -561,35 +593,35 @@ qq.extend(qq.FileUploader.prototype, {
561
593
  e.stopPropagation();
562
594
  },
563
595
  onLeaveNotDescendants: function(e){
564
- qq.removeClass(dropArea, self._classes.dropActive);
596
+ qq.removeClass(dropArea, self._classes.dropActive);
565
597
  },
566
598
  onDrop: function(e){
567
599
  dropArea.style.display = 'none';
568
600
  qq.removeClass(dropArea, self._classes.dropActive);
569
- self._uploadFileList(e.dataTransfer.files);
601
+ self._uploadFileList(e.dataTransfer.files);
570
602
  }
571
603
  });
572
-
604
+
573
605
  dropArea.style.display = 'none';
574
606
 
575
- qq.attach(document, 'dragenter', function(e){
576
- if (!dz._isValidFileDrag(e)) return;
577
-
578
- dropArea.style.display = 'block';
579
- });
607
+ qq.attach(document, 'dragenter', function(e){
608
+ if (!dz._isValidFileDrag(e)) return;
609
+
610
+ dropArea.style.display = 'block';
611
+ });
580
612
  qq.attach(document, 'dragleave', function(e){
581
- if (!dz._isValidFileDrag(e)) return;
582
-
613
+ if (!dz._isValidFileDrag(e)) return;
614
+
583
615
  var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
584
616
  // only fire when leaving document out
585
- if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
586
- dropArea.style.display = 'none';
617
+ if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
618
+ dropArea.style.display = 'none';
587
619
  }
588
- });
620
+ });
589
621
  },
590
622
  _onSubmit: function(id, fileName){
591
623
  qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments);
592
- this._addToList(id, fileName);
624
+ this._addToList(id, fileName);
593
625
  },
594
626
  _onProgress: function(id, fileName, loaded, total){
595
627
  qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
@@ -597,87 +629,106 @@ qq.extend(qq.FileUploader.prototype, {
597
629
  var item = this._getItemByFileId(id);
598
630
  var size = this._find(item, 'size');
599
631
  size.style.display = 'inline';
600
-
601
- var text;
632
+
633
+ var text;
602
634
  if (loaded != total){
603
- text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total);
604
- } else {
635
+ text = Math.round(loaded / total * 100) + '% of ' + this._formatSize(total);
636
+ } else {
605
637
  text = this._formatSize(total);
606
- }
607
-
608
- qq.setText(size, text);
638
+ this._totalSizeText = text;
639
+ }
640
+
641
+ if(this._totalSizeText)
642
+ {
643
+ qq.setText(size, this._totalSizeText);
644
+ }
645
+ else if(text)
646
+ {
647
+ qq.setText(size, text);
648
+ }
649
+ else
650
+ {
651
+ qq.setText(size, "calculating...");
652
+ }
609
653
  },
610
654
  _onComplete: function(id, fileName, result){
611
655
  qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
612
656
 
613
657
  // mark completed
614
- var item = this._getItemByFileId(id);
658
+ var item = this._getItemByFileId(id);
615
659
  qq.remove(this._find(item, 'cancel'));
616
660
  qq.remove(this._find(item, 'spinner'));
617
-
661
+
618
662
  if (result.success){
619
- qq.addClass(item, this._classes.success);
663
+ qq.addClass(item, this._classes.success);
664
+ if(this._totalSizeText)
665
+ {
666
+ var item = this._getItemByFileId(id);
667
+ var size = this._find(item, 'size');
668
+ qq.setText(size, this._totalSizeText);
669
+ this._totalSizeText = '';
670
+ }
620
671
  } else {
621
672
  qq.addClass(item, this._classes.fail);
622
- }
673
+ }
623
674
  },
624
675
  _addToList: function(id, fileName){
625
- var item = qq.toElement(this._options.fileTemplate);
676
+ var item = qq.toElement(this._options.fileTemplate);
626
677
  item.qqFileId = id;
627
678
 
628
- var fileElement = this._find(item, 'file');
679
+ var fileElement = this._find(item, 'file');
629
680
  qq.setText(fileElement, this._formatFileName(fileName));
630
- this._find(item, 'size').style.display = 'none';
681
+ this._find(item, 'size').style.display = 'none';
631
682
 
632
683
  this._listElement.appendChild(item);
633
684
  },
634
685
  _getItemByFileId: function(id){
635
- var item = this._listElement.firstChild;
636
-
686
+ var item = this._listElement.firstChild;
687
+
637
688
  // there can't be txt nodes in dynamically created list
638
689
  // and we can use nextSibling
639
- while (item){
640
- if (item.qqFileId == id) return item;
690
+ while (item){
691
+ if (item.qqFileId == id) return item;
641
692
  item = item.nextSibling;
642
- }
693
+ }
643
694
  },
644
695
  /**
645
- * delegate click event for cancel link
696
+ * delegate click event for cancel link
646
697
  **/
647
698
  _bindCancelEvent: function(){
648
699
  var self = this,
649
- list = this._listElement;
650
-
651
- qq.attach(list, 'click', function(e){
700
+ list = this._listElement;
701
+
702
+ qq.attach(list, 'click', function(e){
652
703
  e = e || window.event;
653
704
  var target = e.target || e.srcElement;
654
-
655
- if (qq.hasClass(target, self._classes.cancel)){
705
+
706
+ if (qq.hasClass(target, self._classes.cancel)){
656
707
  qq.preventDefault(e);
657
-
708
+
658
709
  var item = target.parentNode;
659
710
  self._handler.cancel(item.qqFileId);
660
711
  qq.remove(item);
661
712
  }
662
713
  });
663
- }
714
+ }
664
715
  });
665
-
716
+
666
717
  qq.UploadDropZone = function(o){
667
718
  this._options = {
668
- element: null,
719
+ element: null,
669
720
  onEnter: function(e){},
670
- onLeave: function(e){},
671
- // is not fired when leaving element by hovering descendants
672
- onLeaveNotDescendants: function(e){},
673
- onDrop: function(e){}
721
+ onLeave: function(e){},
722
+ // is not fired when leaving element by hovering descendants
723
+ onLeaveNotDescendants: function(e){},
724
+ onDrop: function(e){}
674
725
  };
675
- qq.extend(this._options, o);
676
-
726
+ qq.extend(this._options, o);
727
+
677
728
  this._element = this._options.element;
678
-
729
+
679
730
  this._disableDropOutside();
680
- this._attachEvents();
731
+ this._attachEvents();
681
732
  };
682
733
 
683
734
  qq.UploadDropZone.prototype = {
@@ -688,84 +739,84 @@ qq.UploadDropZone.prototype = {
688
739
  qq.attach(document, 'dragover', function(e){
689
740
  if (e.dataTransfer){
690
741
  e.dataTransfer.dropEffect = 'none';
691
- e.preventDefault();
692
- }
742
+ e.preventDefault();
743
+ }
693
744
  });
694
-
695
- qq.UploadDropZone.dropOutsideDisabled = true;
696
- }
745
+
746
+ qq.UploadDropZone.dropOutsideDisabled = true;
747
+ }
697
748
  },
698
749
  _attachEvents: function(){
699
- var self = this;
700
-
750
+ var self = this;
751
+
701
752
  qq.attach(self._element, 'dragover', function(e){
702
753
  if (!self._isValidFileDrag(e)) return;
703
-
754
+
704
755
  var effect = e.dataTransfer.effectAllowed;
705
756
  if (effect == 'move' || effect == 'linkMove'){
706
- e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
707
- } else {
757
+ e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
758
+ } else {
708
759
  e.dataTransfer.dropEffect = 'copy'; // for Chrome
709
760
  }
710
-
761
+
711
762
  e.stopPropagation();
712
- e.preventDefault();
763
+ e.preventDefault();
713
764
  });
714
-
765
+
715
766
  qq.attach(self._element, 'dragenter', function(e){
716
767
  if (!self._isValidFileDrag(e)) return;
717
-
768
+
718
769
  self._options.onEnter(e);
719
770
  });
720
-
771
+
721
772
  qq.attach(self._element, 'dragleave', function(e){
722
773
  if (!self._isValidFileDrag(e)) return;
723
-
774
+
724
775
  self._options.onLeave(e);
725
-
726
- var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
776
+
777
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
727
778
  // do not fire when moving a mouse over a descendant
728
779
  if (qq.contains(this, relatedTarget)) return;
729
-
730
- self._options.onLeaveNotDescendants(e);
780
+
781
+ self._options.onLeaveNotDescendants(e);
731
782
  });
732
-
783
+
733
784
  qq.attach(self._element, 'drop', function(e){
734
785
  if (!self._isValidFileDrag(e)) return;
735
-
786
+
736
787
  e.preventDefault();
737
788
  self._options.onDrop(e);
738
- });
789
+ });
739
790
  },
740
791
  _isValidFileDrag: function(e){
741
792
  var dt = e.dataTransfer,
742
- // do not check dt.types.contains in webkit, because it crashes safari 4
743
- isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
793
+ // do not check dt.types.contains in webkit, because it crashes safari 4
794
+ isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
744
795
 
745
796
  // dt.effectAllowed is none in Safari 5
746
- // dt.types.contains check is for firefox
747
- return dt && dt.effectAllowed != 'none' &&
797
+ // dt.types.contains check is for firefox
798
+ return dt && dt.effectAllowed != 'none' &&
748
799
  (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files')));
749
-
750
- }
751
- };
800
+
801
+ }
802
+ };
752
803
 
753
804
  qq.UploadButton = function(o){
754
805
  this._options = {
755
- element: null,
756
- // if set to true adds multiple attribute to file input
806
+ element: null,
807
+ // if set to true adds multiple attribute to file input
757
808
  multiple: false,
758
809
  // name attribute of file input
759
810
  name: 'file',
760
811
  onChange: function(input){},
761
812
  hoverClass: 'qq-upload-button-hover',
762
- focusClass: 'qq-upload-button-focus'
813
+ focusClass: 'qq-upload-button-focus'
763
814
  };
764
-
815
+
765
816
  qq.extend(this._options, o);
766
-
817
+
767
818
  this._element = this._options.element;
768
-
819
+
769
820
  // make button suitable container for input
770
821
  qq.css(this._element, {
771
822
  position: 'relative',
@@ -773,35 +824,35 @@ qq.UploadButton = function(o){
773
824
  // Make sure browse button is in the right side
774
825
  // in Internet Explorer
775
826
  direction: 'ltr'
776
- });
777
-
827
+ });
828
+
778
829
  this._input = this._createInput();
779
830
  };
780
831
 
781
832
  qq.UploadButton.prototype = {
782
- /* returns file input element */
833
+ /* returns file input element */
783
834
  getInput: function(){
784
835
  return this._input;
785
836
  },
786
837
  /* cleans/recreates the file input */
787
838
  reset: function(){
788
839
  if (this._input.parentNode){
789
- qq.remove(this._input);
790
- }
791
-
840
+ qq.remove(this._input);
841
+ }
842
+
792
843
  qq.removeClass(this._element, this._options.focusClass);
793
844
  this._input = this._createInput();
794
- },
795
- _createInput: function(){
845
+ },
846
+ _createInput: function(){
796
847
  var input = document.createElement("input");
797
-
848
+
798
849
  if (this._options.multiple){
799
850
  input.setAttribute("multiple", "multiple");
800
851
  }
801
-
852
+
802
853
  input.setAttribute("type", "file");
803
854
  input.setAttribute("name", this._options.name);
804
-
855
+
805
856
  qq.css(input, {
806
857
  position: 'absolute',
807
858
  // in Opera only 'browse' button
@@ -817,14 +868,14 @@ qq.UploadButton.prototype = {
817
868
  cursor: 'pointer',
818
869
  opacity: 0
819
870
  });
820
-
871
+
821
872
  this._element.appendChild(input);
822
873
 
823
874
  var self = this;
824
875
  qq.attach(input, 'change', function(){
825
876
  self._options.onChange(input);
826
877
  });
827
-
878
+
828
879
  qq.attach(input, 'mouseover', function(){
829
880
  qq.addClass(self._element, self._options.hoverClass);
830
881
  });
@@ -845,8 +896,8 @@ qq.UploadButton.prototype = {
845
896
  input.setAttribute('tabIndex', "-1");
846
897
  }
847
898
 
848
- return input;
849
- }
899
+ return input;
900
+ }
850
901
  };
851
902
 
852
903
  /**
@@ -856,26 +907,26 @@ qq.UploadHandlerAbstract = function(o){
856
907
  this._options = {
857
908
  debug: false,
858
909
  action: '/upload.php',
859
- // maximum number of concurrent uploads
910
+ // maximum number of concurrent uploads
860
911
  maxConnections: 999,
861
912
  onProgress: function(id, fileName, loaded, total){},
862
913
  onComplete: function(id, fileName, response){},
863
914
  onCancel: function(id, fileName){}
864
915
  };
865
- qq.extend(this._options, o);
866
-
916
+ qq.extend(this._options, o);
917
+
867
918
  this._queue = [];
868
919
  // params for files in queue
869
920
  this._params = [];
870
921
  };
871
922
  qq.UploadHandlerAbstract.prototype = {
872
923
  log: function(str){
873
- if (this._options.debug && window.console) console.log('[uploader] ' + str);
924
+ if (this._options.debug && window.console) console.log('[uploader] ' + str);
874
925
  },
875
926
  /**
876
927
  * Adds file or file input to the queue
877
928
  * @returns id
878
- **/
929
+ **/
879
930
  add: function(file){},
880
931
  /**
881
932
  * Sends the file identified by id and additional query params to the server
@@ -883,12 +934,12 @@ qq.UploadHandlerAbstract.prototype = {
883
934
  upload: function(id, params){
884
935
  var len = this._queue.push(id);
885
936
 
886
- var copy = {};
937
+ var copy = {};
887
938
  qq.extend(copy, params);
888
- this._params[id] = copy;
889
-
939
+ this._params[id] = copy;
940
+
890
941
  // if too many active uploads, wait...
891
- if (len <= this._options.maxConnections){
942
+ if (len <= this._options.maxConnections){
892
943
  this._upload(id, this._params[id]);
893
944
  }
894
945
  },
@@ -914,7 +965,7 @@ qq.UploadHandlerAbstract.prototype = {
914
965
  getName: function(id){},
915
966
  /**
916
967
  * Returns size of the file identified by id
917
- */
968
+ */
918
969
  getSize: function(id){},
919
970
  /**
920
971
  * Returns id of files being uploaded or
@@ -930,21 +981,21 @@ qq.UploadHandlerAbstract.prototype = {
930
981
  /**
931
982
  * Actual cancel method
932
983
  */
933
- _cancel: function(id){},
984
+ _cancel: function(id){},
934
985
  /**
935
986
  * Removes element from queue, starts upload of next
936
987
  */
937
988
  _dequeue: function(id){
938
989
  var i = qq.indexOf(this._queue, id);
939
990
  this._queue.splice(i, 1);
940
-
991
+
941
992
  var max = this._options.maxConnections;
942
-
993
+
943
994
  if (this._queue.length >= max && i < max){
944
995
  var nextId = this._queue[max-1];
945
996
  this._upload(nextId, this._params[nextId]);
946
997
  }
947
- }
998
+ }
948
999
  };
949
1000
 
950
1001
  /**
@@ -953,7 +1004,7 @@ qq.UploadHandlerAbstract.prototype = {
953
1004
  */
954
1005
  qq.UploadHandlerForm = function(o){
955
1006
  qq.UploadHandlerAbstract.apply(this, arguments);
956
-
1007
+
957
1008
  this._inputs = {};
958
1009
  };
959
1010
  // @inherits qq.UploadHandlerAbstract
@@ -962,25 +1013,25 @@ qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
962
1013
  qq.extend(qq.UploadHandlerForm.prototype, {
963
1014
  add: function(fileInput){
964
1015
  fileInput.setAttribute('name', 'qqfile');
965
- var id = 'qq-upload-handler-iframe' + qq.getUniqueId();
966
-
1016
+ var id = 'qq-upload-handler-iframe' + qq.getUniqueId();
1017
+
967
1018
  this._inputs[id] = fileInput;
968
-
1019
+
969
1020
  // remove file input from DOM
970
1021
  if (fileInput.parentNode){
971
1022
  qq.remove(fileInput);
972
1023
  }
973
-
1024
+
974
1025
  return id;
975
1026
  },
976
1027
  getName: function(id){
977
1028
  // get input value and remove path to normalize
978
1029
  return this._inputs[id].value.replace(/.*(\/|\\)/, "");
979
- },
1030
+ },
980
1031
  _cancel: function(id){
981
1032
  this._options.onCancel(id, this.getName(id));
982
-
983
- delete this._inputs[id];
1033
+
1034
+ delete this._inputs[id];
984
1035
 
985
1036
  var iframe = document.getElementById(id);
986
1037
  if (iframe){
@@ -991,29 +1042,29 @@ qq.extend(qq.UploadHandlerForm.prototype, {
991
1042
 
992
1043
  qq.remove(iframe);
993
1044
  }
994
- },
995
- _upload: function(id, params){
1045
+ },
1046
+ _upload: function(id, params){
996
1047
  var input = this._inputs[id];
997
-
1048
+
998
1049
  if (!input){
999
1050
  throw new Error('file with passed id was not added, or already uploaded or cancelled');
1000
- }
1051
+ }
1001
1052
 
1002
1053
  var fileName = this.getName(id);
1003
-
1054
+
1004
1055
  var iframe = this._createIframe(id);
1005
1056
  var form = this._createForm(iframe, params);
1006
1057
  form.appendChild(input);
1007
1058
 
1008
1059
  var self = this;
1009
- this._attachLoadEvent(iframe, function(){
1060
+ this._attachLoadEvent(iframe, function(){
1010
1061
  self.log('iframe loaded');
1011
-
1062
+
1012
1063
  var response = self._getIframeContentJSON(iframe);
1013
1064
 
1014
1065
  self._options.onComplete(id, fileName, response);
1015
1066
  self._dequeue(id);
1016
-
1067
+
1017
1068
  delete self._inputs[id];
1018
1069
  // timeout added to fix busy state in FF3.6
1019
1070
  setTimeout(function(){
@@ -1021,11 +1072,11 @@ qq.extend(qq.UploadHandlerForm.prototype, {
1021
1072
  }, 1);
1022
1073
  });
1023
1074
 
1024
- form.submit();
1025
- qq.remove(form);
1026
-
1075
+ form.submit();
1076
+ qq.remove(form);
1077
+
1027
1078
  return id;
1028
- },
1079
+ },
1029
1080
  _attachLoadEvent: function(iframe, callback){
1030
1081
  qq.attach(iframe, 'load', function(){
1031
1082
  // when we remove iframe from dom
@@ -1056,15 +1107,15 @@ qq.extend(qq.UploadHandlerForm.prototype, {
1056
1107
  // iframe.contentWindow.document - for IE<7
1057
1108
  var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document,
1058
1109
  response;
1059
-
1110
+
1060
1111
  this.log("converting iframe's innerHTML to JSON");
1061
- this.log("innerHTML = " + doc.body.innerHTML);
1062
-
1112
+ this.log("innerHTML = " + doc.body.firstChild.innerHTML);
1113
+
1063
1114
  try {
1064
- response = eval("(" + doc.body.innerHTML + ")");
1115
+ response = eval("(" + doc.body.firstChild.innerHTML + ")");
1065
1116
  } catch(err){
1066
1117
  response = {};
1067
- }
1118
+ }
1068
1119
 
1069
1120
  return response;
1070
1121
  },
@@ -1119,66 +1170,66 @@ qq.UploadHandlerXhr = function(o){
1119
1170
 
1120
1171
  this._files = [];
1121
1172
  this._xhrs = [];
1122
-
1123
- // current loaded size in bytes for each file
1173
+
1174
+ // current loaded size in bytes for each file
1124
1175
  this._loaded = [];
1125
1176
  };
1126
1177
 
1127
1178
  // static method
1128
1179
  qq.UploadHandlerXhr.isSupported = function(){
1129
1180
  var input = document.createElement('input');
1130
- input.type = 'file';
1131
-
1181
+ input.type = 'file';
1182
+
1132
1183
  return (
1133
1184
  'multiple' in input &&
1134
1185
  typeof File != "undefined" &&
1135
- typeof (new XMLHttpRequest()).upload != "undefined" );
1186
+ typeof (new XMLHttpRequest()).upload != "undefined" );
1136
1187
  };
1137
1188
 
1138
1189
  // @inherits qq.UploadHandlerAbstract
1139
- qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype)
1190
+ qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype);
1140
1191
 
1141
1192
  qq.extend(qq.UploadHandlerXhr.prototype, {
1142
1193
  /**
1143
1194
  * Adds file to the queue
1144
1195
  * Returns id to use with upload, cancel
1145
- **/
1196
+ **/
1146
1197
  add: function(file){
1147
1198
  if (!(file instanceof File)){
1148
1199
  throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
1149
1200
  }
1150
-
1151
- return this._files.push(file) - 1;
1201
+
1202
+ return this._files.push(file) - 1;
1152
1203
  },
1153
- getName: function(id){
1204
+ getName: function(id){
1154
1205
  var file = this._files[id];
1155
1206
  // fix missing name in Safari 4
1156
- return file.fileName != null ? file.fileName : file.name;
1207
+ return file.fileName != null ? file.fileName : file.name;
1157
1208
  },
1158
1209
  getSize: function(id){
1159
1210
  var file = this._files[id];
1160
1211
  return file.fileSize != null ? file.fileSize : file.size;
1161
- },
1212
+ },
1162
1213
  /**
1163
- * Returns uploaded bytes for file identified by id
1164
- */
1214
+ * Returns uploaded bytes for file identified by id
1215
+ */
1165
1216
  getLoaded: function(id){
1166
- return this._loaded[id] || 0;
1217
+ return this._loaded[id] || 0;
1167
1218
  },
1168
1219
  /**
1169
1220
  * Sends the file identified by id and additional query params to the server
1170
1221
  * @param {Object} params name-value string pairs
1171
- */
1222
+ */
1172
1223
  _upload: function(id, params){
1173
1224
  var file = this._files[id],
1174
1225
  name = this.getName(id),
1175
1226
  size = this.getSize(id);
1176
-
1227
+
1177
1228
  this._loaded[id] = 0;
1178
-
1229
+
1179
1230
  var xhr = this._xhrs[id] = new XMLHttpRequest();
1180
1231
  var self = this;
1181
-
1232
+
1182
1233
  xhr.upload.onprogress = function(e){
1183
1234
  if (e.lengthComputable){
1184
1235
  self._loaded[id] = e.loaded;
@@ -1186,9 +1237,9 @@ qq.extend(qq.UploadHandlerXhr.prototype, {
1186
1237
  }
1187
1238
  };
1188
1239
 
1189
- xhr.onreadystatechange = function(){
1240
+ xhr.onreadystatechange = function(){
1190
1241
  if (xhr.readyState == 4){
1191
- self._onComplete(id, xhr);
1242
+ self._onComplete(id, xhr);
1192
1243
  }
1193
1244
  };
1194
1245
 
@@ -1198,6 +1249,7 @@ qq.extend(qq.UploadHandlerXhr.prototype, {
1198
1249
  var queryString = qq.obj2url(params, this._options.action);
1199
1250
 
1200
1251
  xhr.open("POST", queryString, true);
1252
+ xhr.setRequestHeader("Accept", "text/javascript");
1201
1253
  xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
1202
1254
  xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
1203
1255
  xhr.setRequestHeader("Content-Type", "application/octet-stream");
@@ -1206,42 +1258,42 @@ qq.extend(qq.UploadHandlerXhr.prototype, {
1206
1258
  _onComplete: function(id, xhr){
1207
1259
  // the request was aborted/cancelled
1208
1260
  if (!this._files[id]) return;
1209
-
1261
+
1210
1262
  var name = this.getName(id);
1211
1263
  var size = this.getSize(id);
1212
-
1264
+
1213
1265
  this._options.onProgress(id, name, size, size);
1214
-
1266
+
1215
1267
  if (xhr.status == 200){
1216
1268
  this.log("xhr - server response received");
1217
1269
  this.log("responseText = " + xhr.responseText);
1218
-
1270
+
1219
1271
  var response;
1220
-
1272
+
1221
1273
  try {
1222
1274
  response = eval("(" + xhr.responseText + ")");
1223
1275
  } catch(err){
1224
1276
  response = {};
1225
1277
  }
1226
-
1278
+
1227
1279
  this._options.onComplete(id, name, response);
1228
-
1229
- } else {
1280
+
1281
+ } else {
1230
1282
  this._options.onComplete(id, name, {});
1231
1283
  }
1232
-
1284
+
1233
1285
  this._files[id] = null;
1234
- this._xhrs[id] = null;
1235
- this._dequeue(id);
1286
+ this._xhrs[id] = null;
1287
+ this._dequeue(id);
1236
1288
  },
1237
1289
  _cancel: function(id){
1238
1290
  this._options.onCancel(id, this.getName(id));
1239
-
1291
+
1240
1292
  this._files[id] = null;
1241
-
1293
+
1242
1294
  if (this._xhrs[id]){
1243
1295
  this._xhrs[id].abort();
1244
- this._xhrs[id] = null;
1296
+ this._xhrs[id] = null;
1245
1297
  }
1246
1298
  }
1247
1299
  });