h5_uploader 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  });