Subversion Repositories eFlore/Applications.cel

Rev

Rev 1847 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1845 aurelien 1
/**
2
 * This namespace should be in another file but I dicided to put it here for consistancy.
3
 */
4
Ext.namespace('Ext.ux.Utils');
5
 
6
/**
7
 * This class implements event queue behaviour.
8
 *
9
 * @class Ext.ux.Utils.EventQueue
10
 * @param function  handler  Event handler.
11
 * @param object    scope    Handler scope.
12
 */
13
Ext.ux.Utils.EventQueue = function(handler, scope)
14
{
15
  if (!handler) {
16
    throw 'Handler is required.';
17
  }
18
  this.handler = handler;
19
  this.scope = scope || window;
20
  this.queue = [];
21
  this.is_processing = false;
22
 
23
  /**
24
   * Posts event into the queue.
25
   *
26
   * @access public
27
   * @param mixed event Event identificator.
28
   * @param mixed data  Event data.
29
   */
30
  this.postEvent = function(event, data)
31
  {
32
    data = data || null;
33
    this.queue.push({event: event, data: data});
34
    if (!this.is_processing) {
35
      this.process();
36
    }
37
  }
38
 
39
  this.flushEventQueue = function()
40
  {
41
    this.queue = [];
42
  },
43
 
44
  /**
45
   * @access private
46
   */
47
  this.process = function()
48
  {
49
    while (this.queue.length > 0) {
50
      this.is_processing = true;
51
      var event_data = this.queue.shift();
52
      this.handler.call(this.scope, event_data.event, event_data.data);
53
    }
54
    this.is_processing = false;
55
  }
56
}
57
 
58
/**
59
 * This class implements Mili's finite state automata behaviour.
60
 *
61
 *  Transition / output table format:
62
 *  {
63
 *    'state_1' : {
64
 *      'event_1' : [
65
 *        {
66
 *          p|predicate: function,    // Transition predicate, optional, default to true.
67
 *                                    // If array then conjunction will be applyed to the operands.
68
 *                                    // Predicate signature is (data, event, this).
69
 *          a|action: function|array, // Transition action, optional, default to Ext.emptyFn.
70
 *                                    // If array then methods will be called sequentially.
71
 *                                    // Action signature is (data, event, this).
72
 *          s|state: 'state_x',       // New state - transition destination, optional, default to
73
 *                                    // current state.
74
 *          scope: object             // Predicate and action scope, optional, default to
75
 *                                    // trans_table_scope or window.
76
 *        }
77
 *      ]
78
 *    },
79
 *
80
 *    'state_2' : {
81
 *      ...
82
 *    }
83
 *    ...
84
 *  }
85
 *
86
 *  @param  mixed initial_state Initial state.
87
 *  @param  object trans_table Transition / output table.
88
 *  @param  trans_table_scope Transition / output table's methods scope.
89
 */
90
Ext.ux.Utils.FSA = function(initial_state, trans_table, trans_table_scope)
91
{
92
  this.current_state = initial_state;
93
  this.trans_table = trans_table || {};
94
  this.trans_table_scope = trans_table_scope || window;
95
  Ext.ux.Utils.FSA.superclass.constructor.call(this, this.processEvent, this);
96
}
97
 
98
Ext.extend(Ext.ux.Utils.FSA, Ext.ux.Utils.EventQueue, {
99
 
100
  current_state : null,
101
  trans_table : null,
102
  trans_table_scope : null,
103
 
104
  /**
105
   * Returns current state
106
   *
107
   * @access public
108
   * @return mixed Current state.
109
   */
110
  state : function()
111
  {
112
    return this.current_state;
113
  },
114
 
115
  /**
116
   * @access public
117
   */
118
  processEvent : function(event, data)
119
  {
120
    var transitions = this.currentStateEventTransitions(event);
121
    if (!transitions) {
122
      throw "State '" + this.current_state + "' has no transition for event '" + event + "'.";
123
    }
124
    for (var i = 0, len = transitions.length; i < len; i++) {
125
      var transition = transitions[i];
126
 
127
      var predicate = transition.predicate || transition.p || true;
128
      var action = transition.action || transition.a || Ext.emptyFn;
129
      var new_state = transition.state || transition.s || this.current_state;
130
      var scope = transition.scope || this.trans_table_scope;
131
 
132
      if (this.computePredicate(predicate, scope, data, event)) {
133
        this.callAction(action, scope, data, event);
134
        this.current_state = new_state;
135
        return;
136
      }
137
    }
138
 
139
    throw "State '" + this.current_state + "' has no transition for event '" + event + "' in current context";
140
  },
141
 
142
  /**
143
   * @access private
144
   */
145
  currentStateEventTransitions : function(event)
146
  {
147
    return this.trans_table[this.current_state] ?
148
      this.trans_table[this.current_state][event] || false
149
      :
150
      false;
151
  },
152
 
153
  /**
154
   * @access private
155
   */
156
  computePredicate : function(predicate, scope, data, event)
157
  {
158
    var result = false;
159
 
160
    switch (Ext.type(predicate)) {
161
     case 'function':
162
       result = predicate.call(scope, data, event, this);
163
       break;
164
     case 'array':
165
       result = true;
166
       for (var i = 0, len = predicate.length; result && (i < len); i++) {
167
         if (Ext.type(predicate[i]) == 'function') {
168
           result = predicate[i].call(scope, data, event, this);
169
         }
170
         else {
171
           throw [
172
             'Predicate: ',
173
             predicate[i],
174
             ' is not callable in "',
175
             this.current_state,
176
             '" state for event "',
177
             event
178
           ].join('');
179
         }
180
       }
181
       break;
182
     case 'boolean':
183
       result = predicate;
184
       break;
185
     default:
186
       throw [
187
         'Predicate: ',
188
         predicate,
189
         ' is not callable in "',
190
         this.current_state,
191
         '" state for event "',
192
         event
193
       ].join('');
194
    }
195
    return result;
196
  },
197
 
198
  /**
199
   * @access private
200
   */
201
  callAction : function(action, scope, data, event)
202
  {
203
    switch (Ext.type(action)) {
204
       case 'array':
205
       for (var i = 0, len = action.length; i < len; i++) {
206
         if (Ext.type(action[i]) == 'function') {
207
           action[i].call(scope, data, event, this);
208
         }
209
         else {
210
           throw [
211
             'Action: ',
212
             action[i],
213
             ' is not callable in "',
214
             this.current_state,
215
             '" state for event "',
216
             event
217
           ].join('');
218
         }
219
       }
220
         break;
221
     case 'function':
222
       action.call(scope, data, event, this);
223
       break;
224
     default:
225
       throw [
226
         'Action: ',
227
         action,
228
         ' is not callable in "',
229
         this.current_state,
230
         '" state for event "',
231
         event
232
       ].join('');
233
    }
234
  }
235
});
236
 
237
// ---------------------------------------------------------------------------------------------- //
238
 
239
/**
240
 * Ext.ux.UploadDialog namespace.
241
 */
242
Ext.namespace('Ext.ux.UploadDialog');
243
 
244
/**
245
 * File upload browse button.
246
 *
247
 * @class Ext.ux.UploadDialog.BrowseButton
248
 */
249
Ext.ux.UploadDialog.BrowseButton = Ext.extend(Ext.Button,
250
{
251
  input_name : 'file',
252
 
253
  input_file : null,
254
 
255
  original_handler : null,
256
 
257
  original_scope : null,
258
 
259
  /**
260
   * @access private
261
   */
262
  initComponent : function()
263
  {
264
    Ext.ux.UploadDialog.BrowseButton.superclass.initComponent.call(this);
265
    this.original_handler = this.handler || null;
266
    this.original_scope = this.scope || window;
267
    this.handler = null;
268
    this.scope = null;
269
  },
270
 
271
  /**
272
   * @access private
273
   */
274
  onRender : function(ct, position)
275
  {
276
    Ext.ux.UploadDialog.BrowseButton.superclass.onRender.call(this, ct, position);
277
    this.createInputFile();
278
  },
279
 
280
  /**
281
   * @access private
282
   */
283
  createInputFile : function()
284
  {
285
    var button_container = this.el.child('.x-btn-center');
286
    button_container.position('relative');
287
    this.input_file = Ext.DomHelper.append(
288
      button_container,
289
      {
290
        tag: 'input',
291
        type: 'file',
292
        size: 1,
293
        name: this.input_name || Ext.id(this.el),
294
        style: 'position: absolute; display: block; border: none; cursor: pointer'
295
      },
296
      true
297
    );
298
 
299
    var button_box = button_container.getBox();
300
    this.input_file.setStyle('font-size', (button_box.width * 0.5) + 'px');
301
 
302
    var input_box = this.input_file.getBox();
303
    var adj = {x: 3, y: 3}
304
    if (Ext.isIE) {
305
      adj = {x: 0, y: 3}
306
    }
307
 
308
    this.input_file.setLeft(button_box.width - input_box.width + adj.x + 'px');
309
    this.input_file.setTop(button_box.height - input_box.height + adj.y + 'px');
310
    this.input_file.setOpacity(0.0);
311
 
312
    if (this.handleMouseEvents) {
313
      this.input_file.on('mouseover', this.onMouseOver, this);
314
        this.input_file.on('mousedown', this.onMouseDown, this);
315
    }
316
 
317
    if(this.tooltip){
318
      if(typeof this.tooltip == 'object'){
319
        Ext.QuickTips.register(Ext.apply({target: this.input_file}, this.tooltip));
320
      }
321
      else {
322
        this.input_file.dom[this.tooltipType] = this.tooltip;
323
        }
324
      }
325
 
326
    this.input_file.on('change', this.onInputFileChange, this);
327
    this.input_file.on('click', function(e) { e.stopPropagation(); });
328
  },
329
 
330
  /**
331
   * @access public
332
   */
333
  detachInputFile : function(no_create)
334
  {
335
    var result = this.input_file;
336
 
337
    no_create = no_create || false;
338
 
339
    if (typeof this.tooltip == 'object') {
340
      Ext.QuickTips.unregister(this.input_file);
341
    }
342
    else {
343
      this.input_file.dom[this.tooltipType] = null;
344
    }
345
    this.input_file.removeAllListeners();
346
    this.input_file = null;
347
 
348
    if (!no_create) {
349
      this.createInputFile();
350
    }
351
    return result;
352
  },
353
 
354
  /**
355
   * @access public
356
   */
357
  getInputFile : function()
358
  {
359
    return this.input_file;
360
  },
361
 
362
  /**
363
   * @access public
364
   */
365
  disable : function()
366
  {
367
    Ext.ux.UploadDialog.BrowseButton.superclass.disable.call(this);
368
    this.input_file.dom.disabled = true;
369
  },
370
 
371
  /**
372
   * @access public
373
   */
374
  enable : function()
375
  {
376
    Ext.ux.UploadDialog.BrowseButton.superclass.enable.call(this);
377
    this.input_file.dom.disabled = false;
378
  },
379
 
380
  /**
381
   * @access public
382
   */
383
  destroy : function()
384
  {
385
    var input_file = this.detachInputFile(true);
386
    input_file.remove();
387
    input_file = null;
388
    Ext.ux.UploadDialog.BrowseButton.superclass.destroy.call(this);
389
  },
390
 
391
  /**
392
   * @access private
393
   */
394
  onInputFileChange : function()
395
  {
396
    if (this.original_handler) {
397
      this.original_handler.call(this.original_scope, this);
398
    }
399
  }
400
});
401
 
402
/**
403
 * Toolbar file upload browse button.
404
 *
405
 * @class Ext.ux.UploadDialog.TBBrowseButton
406
 */
407
Ext.ux.UploadDialog.TBBrowseButton = Ext.extend(Ext.ux.UploadDialog.BrowseButton,
408
{
409
  hideParent : true,
410
 
411
  onDestroy : function()
412
  {
413
    Ext.ux.UploadDialog.TBBrowseButton.superclass.onDestroy.call(this);
414
    if(this.container) {
415
      this.container.remove();
416
      }
417
  }
418
});
419
 
420
/**
421
 * Record type for dialogs grid.
422
 *
423
 * @class Ext.ux.UploadDialog.FileRecord
424
 */
425
Ext.ux.UploadDialog.FileRecord = Ext.data.Record.create([
426
  {name: 'filename'},
427
  {name: 'state', type: 'int'},
428
  {name: 'note'},
429
  {name: 'input_element'}
430
]);
431
 
432
Ext.ux.UploadDialog.FileRecord.STATE_QUEUE = 0;
433
Ext.ux.UploadDialog.FileRecord.STATE_FINISHED = 1;
434
Ext.ux.UploadDialog.FileRecord.STATE_FAILED = 2;
435
Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING = 3;
436
 
437
/**
438
 * Dialog class.
439
 *
440
 * @class Ext.ux.UploadDialog.Dialog
441
 */
442
Ext.ux.UploadDialog.Dialog = function(config)
443
{
444
  var default_config = {
445
    border: false,
446
    width: 450,
447
    height: 300,
448
    minWidth: 450,
449
    minHeight: 300,
450
    plain: true,
451
    constrainHeader: true,
452
    draggable: true,
453
    closable: true,
454
    maximizable: false,
455
    minimizable: false,
456
    resizable: true,
457
    autoDestroy: true,
458
    closeAction: 'hide',
459
    title: this.i18n.title,
460
    cls: 'ext-ux-uploaddialog-dialog',
461
    // --------
462
    url: '',
463
    base_params: {},
464
    permitted_extensions: [],
465
    reset_on_hide: true,
466
    allow_close_on_upload: false,
467
    upload_autostart: false,
468
    post_var_name: 'file'
469
  }
470
  config = Ext.applyIf(config || {}, default_config);
471
  config.layout = 'absolute';
472
 
473
  Ext.ux.UploadDialog.Dialog.superclass.constructor.call(this, config);
474
}
475
 
476
Ext.extend(Ext.ux.UploadDialog.Dialog, Ext.Window, {
477
 
478
  fsa : null,
479
 
480
  state_tpl : null,
481
 
482
  form : null,
483
 
484
  grid_panel : null,
485
 
486
  progress_bar : null,
487
 
488
  is_uploading : false,
489
 
490
  initial_queued_count : 0,
491
 
492
  upload_frame : null,
493
 
494
  /**
495
   * @access private
496
   */
497
  //--------------------------------------------------------------------------------------------- //
498
  initComponent : function()
499
  {
500
    Ext.ux.UploadDialog.Dialog.superclass.initComponent.call(this);
501
 
502
    // Setting automata protocol
503
    var tt = {
504
      // --------------
505
      'created' : {
506
      // --------------
507
        'window-render' : [
508
          {
509
            action: [this.createForm, this.createProgressBar, this.createGrid],
510
            state: 'rendering'
511
          }
512
        ],
513
        'destroy' : [
514
          {
515
            action: this.flushEventQueue,
516
            state: 'destroyed'
517
          }
518
        ]
519
      },
520
      // --------------
521
      'rendering' : {
522
      // --------------
523
        'grid-render' : [
524
          {
525
            action: [this.fillToolbar, this.updateToolbar],
526
            state: 'ready'
527
          }
528
        ],
529
        'destroy' : [
530
          {
531
            action: this.flushEventQueue,
532
            state: 'destroyed'
533
          }
534
        ]
535
      },
536
      // --------------
537
      'ready' : {
538
      // --------------
539
        'file-selected' : [
540
          {
541
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
542
            action: this.addFileToUploadQueue,
543
            state: 'adding-file'
544
          },
545
          {
546
            // If file is not permitted then do nothing.
547
          }
548
        ],
549
        'grid-selection-change' : [
550
          {
551
            action: this.updateToolbar
552
          }
553
        ],
554
        'remove-files' : [
555
          {
556
            action: [this.removeFiles, this.fireFileRemoveEvent]
557
          }
558
        ],
559
        'reset-queue' : [
560
          {
561
            action: [this.resetQueue, this.fireResetQueueEvent]
562
          }
563
        ],
564
        'start-upload' : [
565
          {
566
            predicate: this.hasUnuploadedFiles,
567
            action: [
568
              this.setUploadingFlag, this.saveInitialQueuedCount, this.updateToolbar,
569
              this.updateProgressBar, this.prepareNextUploadTask, this.fireUploadStartEvent
570
            ],
571
            state: 'uploading'
572
          },
573
          {
574
            // Has nothing to upload, do nothing.
575
          }
576
        ],
577
        'stop-upload' : [
578
          {
579
            // We are not uploading, do nothing. Can be posted by user only at this state.
580
          }
581
        ],
582
        'hide' : [
583
          {
584
            predicate: [this.isNotEmptyQueue, this.getResetOnHide],
585
            action: [this.resetQueue, this.fireResetQueueEvent]
586
          },
587
          {
588
            // Do nothing
589
          }
590
        ],
591
        'destroy' : [
592
          {
593
            action: this.flushEventQueue,
594
            state: 'destroyed'
595
          }
596
        ]
597
      },
598
      // --------------
599
      'adding-file' : {
600
      // --------------
601
        'file-added' : [
602
          {
603
            predicate: this.isUploading,
604
            action: [this.incInitialQueuedCount, this.updateProgressBar, this.fireFileAddEvent],
605
            state: 'uploading'
606
          },
607
          {
608
            predicate: this.getUploadAutostart,
609
            action: [this.startUpload, this.fireFileAddEvent],
610
            state: 'ready'
611
          },
612
          {
613
            action: [this.updateToolbar, this.fireFileAddEvent],
614
            state: 'ready'
615
          }
616
        ]
617
      },
618
      // --------------
619
      'uploading' : {
620
      // --------------
621
        'file-selected' : [
622
          {
623
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
624
            action: this.addFileToUploadQueue,
625
            state: 'adding-file'
626
          },
627
          {
628
            // If file is not permitted then do nothing.
629
          }
630
        ],
631
        'grid-selection-change' : [
632
          {
633
            // Do nothing.
634
          }
635
        ],
636
        'start-upload' : [
637
          {
638
            // Can be posted only by user in this state.
639
          }
640
        ],
641
        'stop-upload' : [
642
          {
643
            predicate: this.hasUnuploadedFiles,
644
            action: [
645
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar,
646
              this.updateProgressBar, this.fireUploadStopEvent
647
            ],
648
            state: 'ready'
649
          },
650
          {
651
            action: [
652
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar,
653
              this.updateProgressBar, this.fireUploadStopEvent, this.fireUploadCompleteEvent
654
            ],
655
            state: 'ready'
656
          }
657
        ],
658
        'file-upload-start' : [
659
          {
660
            action: [this.uploadFile, this.findUploadFrame, this.fireFileUploadStartEvent]
661
          }
662
        ],
663
        'file-upload-success' : [
664
          {
665
            predicate: this.hasUnuploadedFiles,
666
            action: [
667
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar,
668
              this.prepareNextUploadTask, this.fireUploadSuccessEvent
669
            ]
670
          },
671
          {
672
            action: [
673
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState,
674
              this.updateToolbar, this.updateProgressBar, this.fireUploadSuccessEvent,
675
              this.fireUploadCompleteEvent
676
            ],
677
            state: 'ready'
678
          }
679
        ],
680
        'file-upload-error' : [
681
          {
682
            predicate: this.hasUnuploadedFiles,
683
            action: [
684
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar,
685
              this.prepareNextUploadTask, this.fireUploadErrorEvent
686
            ]
687
          },
688
          {
689
            action: [
690
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState,
691
              this.updateToolbar, this.updateProgressBar, this.fireUploadErrorEvent,
692
              this.fireUploadCompleteEvent
693
            ],
694
            state: 'ready'
695
          }
696
        ],
697
        'file-upload-failed' : [
698
          {
699
            predicate: this.hasUnuploadedFiles,
700
            action: [
701
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar,
702
              this.prepareNextUploadTask, this.fireUploadFailedEvent
703
            ]
704
          },
705
          {
706
            action: [
707
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState,
708
              this.updateToolbar, this.updateProgressBar, this.fireUploadFailedEvent,
709
              this.fireUploadCompleteEvent
710
            ],
711
            state: 'ready'
712
          }
713
        ],
714
        'hide' : [
715
          {
716
            predicate: this.getResetOnHide,
717
            action: [this.stopUpload, this.repostHide]
718
          },
719
          {
720
            // Do nothing.
721
          }
722
        ],
723
        'destroy' : [
724
          {
725
            predicate: this.hasUnuploadedFiles,
726
            action: [
727
              this.resetUploadingFlag, this.abortUpload,
728
              this.fireUploadStopEvent, this.flushEventQueue
729
            ],
730
            state: 'destroyed'
731
          },
732
          {
733
            action: [
734
              this.resetUploadingFlag, this.abortUpload,
735
              this.fireUploadStopEvent, this.fireUploadCompleteEvent, this.flushEventQueue
736
            ],
737
            state: 'destroyed'
738
          }
739
        ]
740
      },
741
      // --------------
742
      'destroyed' : {
743
      // --------------
744
      }
745
    }
746
    this.fsa = new Ext.ux.Utils.FSA('created', tt, this);
747
 
748
    // Registering dialog events.
749
    this.addEvents({
750
      'filetest': true,
751
      'fileadd' : true,
752
      'fileremove' : true,
753
      'resetqueue' : true,
754
      'uploadsuccess' : true,
755
      'uploaderror' : true,
756
      'uploadfailed' : true,
757
      'uploadstart' : true,
758
      'uploadstop' : true,
759
      'uploadcomplete' : true,
760
      'fileuploadstart' : true
761
    });
762
 
763
    // Attaching to window events.
764
    this.on('render', this.onWindowRender, this);
765
    this.on('beforehide', this.onWindowBeforeHide, this);
766
    this.on('hide', this.onWindowHide, this);
767
    this.on('destroy', this.onWindowDestroy, this);
768
 
769
    // Compiling state template.
770
    this.state_tpl = new Ext.Template(
771
      "<div class='ext-ux-uploaddialog-state ext-ux-uploaddialog-state-{state}'>&#160;</div>"
772
    ).compile();
773
  },
774
 
775
  createForm : function()
776
  {
777
    this.form = Ext.DomHelper.append(this.body, {
778
      tag: 'form',
779
      method: 'post',
780
      action: this.url,
781
      style: 'position: absolute; left: -100px; top: -100px; width: 100px; height: 100px'
782
    });
783
  },
784
 
785
  createProgressBar : function()
786
  {
787
    this.progress_bar = this.add(
788
      new Ext.ProgressBar({
789
        x: 0,
790
        y: 0,
791
        anchor: '0',
792
        value: 0.0,
793
        text: this.i18n.progress_waiting_text
794
      })
795
    );
796
  },
797
 
798
  createGrid : function()
799
  {
800
    var store = new Ext.data.Store({
801
      proxy: new Ext.data.MemoryProxy([]),
802
      reader: new Ext.data.JsonReader({}, Ext.ux.UploadDialog.FileRecord),
803
      sortInfo: {field: 'state', direction: 'DESC'},
804
      pruneModifiedRecords: true
805
    });
806
 
807
    var cm = new Ext.grid.ColumnModel([
808
      {
809
        header: this.i18n.state_col_title,
810
        width: this.i18n.state_col_width,
811
        resizable: false,
812
        dataIndex: 'state',
813
        sortable: true,
814
        renderer: this.renderStateCell.createDelegate(this)
815
      },
816
      {
817
        header: this.i18n.filename_col_title,
818
        width: this.i18n.filename_col_width,
819
        dataIndex: 'filename',
820
        sortable: true,
821
        renderer: this.renderFilenameCell.createDelegate(this)
822
      },
823
      {
824
        header: this.i18n.note_col_title,
825
        width: this.i18n.note_col_width,
826
        dataIndex: 'note',
827
        sortable: true,
828
        renderer: this.renderNoteCell.createDelegate(this)
829
      }
830
    ]);
831
 
832
      this.grid_panel = new Ext.grid.GridPanel({
833
      ds: store,
834
      cm: cm,
835
 
836
      x: 0,
837
      y: 22,
838
      anchor: '0 -22',
839
      border: true,
840
 
841
        viewConfig: {
842
        autoFill: true,
843
          forceFit: true
844
        },
845
 
846
      bbar : new Ext.Toolbar()
847
    });
848
    this.grid_panel.on('render', this.onGridRender, this);
849
 
850
    this.add(this.grid_panel);
851
 
852
    this.grid_panel.getSelectionModel().on('selectionchange', this.onGridSelectionChange, this);
853
  },
854
 
855
  fillToolbar : function()
856
  {
857
    var tb = this.grid_panel.getBottomToolbar();
858
    tb.x_buttons = {}
859
 
860
    tb.x_buttons.add = tb.addItem(new Ext.ux.UploadDialog.TBBrowseButton({
861
      input_name: this.post_var_name,
862
      text: this.i18n.add_btn_text,
863
      tooltip: this.i18n.add_btn_tip,
864
      iconCls: 'ext-ux-uploaddialog-addbtn',
865
      handler: this.onAddButtonFileSelected,
866
      scope: this
867
    }));
868
 
869
    tb.x_buttons.remove = tb.addButton({
870
      text: this.i18n.remove_btn_text,
871
      tooltip: this.i18n.remove_btn_tip,
872
      iconCls: 'ext-ux-uploaddialog-removebtn',
873
      handler: this.onRemoveButtonClick,
874
      scope: this
875
    });
876
 
877
    tb.x_buttons.reset = tb.addButton({
878
      text: this.i18n.reset_btn_text,
879
      tooltip: this.i18n.reset_btn_tip,
880
      iconCls: 'ext-ux-uploaddialog-resetbtn',
881
      handler: this.onResetButtonClick,
882
      scope: this
883
    });
884
 
885
    tb.add('-');
886
 
887
    tb.x_buttons.upload = tb.addButton({
888
      text: this.i18n.upload_btn_start_text,
889
      tooltip: this.i18n.upload_btn_start_tip,
890
      iconCls: 'ext-ux-uploaddialog-uploadstartbtn',
891
      handler: this.onUploadButtonClick,
892
      scope: this
893
    });
894
 
895
    tb.add('-');
896
 
897
    tb.x_buttons.indicator = tb.addItem(
898
      new Ext.Toolbar.Item(
899
        Ext.DomHelper.append(tb.getEl(), {
900
          tag: 'div',
901
          cls: 'ext-ux-uploaddialog-indicator-stoped',
902
          html: '&#160'
903
        })
904
      )
905
    );
906
 
907
    tb.add('->');
908
 
909
    tb.x_buttons.close = tb.addButton({
910
      text: this.i18n.close_btn_text,
911
      tooltip: this.i18n.close_btn_tip,
912
      handler: this.onCloseButtonClick,
913
      scope: this
914
    });
915
  },
916
 
917
  renderStateCell : function(data, cell, record, row_index, column_index, store)
918
  {
919
    return this.state_tpl.apply({state: data});
920
  },
921
 
922
  renderFilenameCell : function(data, cell, record, row_index, column_index, store)
923
  {
924
    var view = this.grid_panel.getView();
925
    var f = function() {
926
      try {
927
        Ext.fly(
928
          view.getCell(row_index, column_index)
929
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
930
      }
931
      catch (e)
932
      {}
933
    }
934
    f.defer(1000);
935
    return data;
936
  },
937
 
938
  renderNoteCell : function(data, cell, record, row_index, column_index, store)
939
  {
940
    var view = this.grid_panel.getView();
941
    var f = function() {
942
      try {
943
        Ext.fly(
944
          view.getCell(row_index, column_index)
945
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
946
      }
947
      catch (e)
948
      {}
949
      }
950
    f.defer(1000);
951
    return data;
952
  },
953
 
954
  getFileExtension : function(filename)
955
  {
956
    var result = null;
957
    var parts = filename.split('.');
958
    if (parts.length > 1) {
959
      result = parts.pop();
960
    }
961
    return result;
962
  },
963
 
964
  isPermittedFileType : function(filename)
965
  {
966
    var result = true;
967
    if (this.permitted_extensions.length > 0) {
968
      result = this.permitted_extensions.indexOf(this.getFileExtension(filename)) != -1;
969
    }
970
    return result;
971
  },
972
 
973
  isPermittedFile : function(browse_btn)
974
  {
975
    var result = false;
976
    var filename = browse_btn.getInputFile().dom.value;
977
 
978
    if (this.isPermittedFileType(filename)) {
979
      result = true;
980
    }
981
    else {
982
      Ext.Msg.alert(
983
        this.i18n.error_msgbox_title,
984
        String.format(
985
          this.i18n.err_file_type_not_permitted,
986
          filename,
987
          this.permitted_extensions.join(this.i18n.permitted_extensions_join_str)
988
        )
989
      );
990
      result = false;
991
    }
992
 
993
    return result;
994
  },
995
 
996
  fireFileTestEvent : function(browse_btn)
997
  {
998
    return this.fireEvent('filetest', this, browse_btn.getInputFile().dom.value) !== false;
999
  },
1000
 
1001
  addFileToUploadQueue : function(browse_btn)
1002
  {
1003
    var input_file = browse_btn.detachInputFile();
1004
 
1005
    input_file.appendTo(this.form);
1006
    input_file.setStyle('width', '100px');
1007
    input_file.dom.disabled = true;
1008
 
1009
    var store = this.grid_panel.getStore();
1010
    store.add(
1011
      new Ext.ux.UploadDialog.FileRecord({
1012
          state: Ext.ux.UploadDialog.FileRecord.STATE_QUEUE,
1013
          filename: input_file.dom.value,
1014
          note: this.i18n.note_queued_to_upload,
1015
          input_element: input_file
1016
        })
1017
      );
1018
    this.fsa.postEvent('file-added', input_file.dom.value);
1019
  },
1020
 
1021
  fireFileAddEvent : function(filename)
1022
  {
1023
    this.fireEvent('fileadd', this, filename);
1024
  },
1025
 
1026
  updateProgressBar : function()
1027
  {
1028
    if (this.is_uploading) {
1029
      var queued = this.getQueuedCount(true);
1030
      var value = 1 - queued / this.initial_queued_count;
1031
      this.progress_bar.updateProgress(
1032
        value,
1033
        String.format(
1034
          this.i18n.progress_uploading_text,
1035
          this.initial_queued_count - queued,
1036
          this.initial_queued_count
1037
        )
1038
      );
1039
    }
1040
    else {
1041
      this.progress_bar.updateProgress(0, this.i18n.progress_waiting_text);
1042
    }
1043
  },
1044
 
1045
  updateToolbar : function()
1046
  {
1047
    var tb = this.grid_panel.getBottomToolbar();
1048
    if (this.is_uploading) {
1049
      tb.x_buttons.remove.disable();
1050
      tb.x_buttons.reset.disable();
1051
      tb.x_buttons.upload.enable();
1052
      if (!this.getAllowCloseOnUpload()) {
1053
        tb.x_buttons.close.disable();
1054
      }
1055
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
1056
        'ext-ux-uploaddialog-indicator-stoped',
1057
        'ext-ux-uploaddialog-indicator-processing'
1058
      );
1059
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstopbtn');
1060
      tb.x_buttons.upload.setText(this.i18n.upload_btn_stop_text);
1061
      tb.x_buttons.upload.getEl()
1062
        .child(tb.x_buttons.upload.buttonSelector)
1063
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_stop_tip;
1064
    }
1065
    else {
1066
      tb.x_buttons.remove.enable();
1067
      tb.x_buttons.reset.enable();
1068
      tb.x_buttons.close.enable();
1069
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
1070
        'ext-ux-uploaddialog-indicator-processing',
1071
        'ext-ux-uploaddialog-indicator-stoped'
1072
      );
1073
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstartbtn');
1074
      tb.x_buttons.upload.setText(this.i18n.upload_btn_start_text);
1075
      tb.x_buttons.upload.getEl()
1076
        .child(tb.x_buttons.upload.buttonSelector)
1077
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_start_tip;
1078
 
1079
      if (this.getQueuedCount() > 0) {
1080
        tb.x_buttons.upload.enable();
1081
      }
1082
      else {
1083
        tb.x_buttons.upload.disable();
1084
      }
1085
 
1086
      if (this.grid_panel.getSelectionModel().hasSelection()) {
1087
        tb.x_buttons.remove.enable();
1088
      }
1089
      else {
1090
        tb.x_buttons.remove.disable();
1091
      }
1092
 
1093
      if (this.grid_panel.getStore().getCount() > 0) {
1094
        tb.x_buttons.reset.enable();
1095
      }
1096
      else {
1097
        tb.x_buttons.reset.disable();
1098
      }
1099
    }
1100
  },
1101
 
1102
  saveInitialQueuedCount : function()
1103
  {
1104
    this.initial_queued_count = this.getQueuedCount();
1105
  },
1106
 
1107
  incInitialQueuedCount : function()
1108
  {
1109
    this.initial_queued_count++;
1110
  },
1111
 
1112
  setUploadingFlag : function()
1113
  {
1114
    this.is_uploading = true;
1115
  },
1116
 
1117
  resetUploadingFlag : function()
1118
  {
1119
    this.is_uploading = false;
1120
  },
1121
 
1122
  prepareNextUploadTask : function()
1123
  {
1124
    // Searching for first unuploaded file.
1125
    var store = this.grid_panel.getStore();
1126
    var record = null;
1127
 
1128
    store.each(function(r) {
1129
      if (!record && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
1130
        record = r;
1131
      }
1132
      else {
1133
        r.get('input_element').dom.disabled = true;
1134
      }
1135
    });
1136
 
1137
    record.get('input_element').dom.disabled = false;
1138
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING);
1139
    record.set('note', this.i18n.note_processing);
1140
    record.commit();
1141
 
1142
    this.fsa.postEvent('file-upload-start', record);
1143
  },
1144
 
1145
  fireUploadStartEvent : function()
1146
  {
1147
    this.fireEvent('uploadstart', this);
1148
  },
1149
 
1150
  removeFiles : function(file_records)
1151
  {
1152
    var store = this.grid_panel.getStore();
1153
    for (var i = 0, len = file_records.length; i < len; i++) {
1154
      var r = file_records[i];
1155
      r.get('input_element').remove();
1156
      store.remove(r);
1157
    }
1158
  },
1159
 
1160
  fireFileRemoveEvent : function(file_records)
1161
  {
1162
    for (var i = 0, len = file_records.length; i < len; i++) {
1163
      this.fireEvent('fileremove', this, file_records[i].get('filename'));
1164
    }
1165
  },
1166
 
1167
  resetQueue : function()
1168
  {
1169
    var store = this.grid_panel.getStore();
1170
    store.each(
1171
      function(r) {
1172
        r.get('input_element').remove();
1173
      }
1174
    );
1175
    store.removeAll();
1176
  },
1177
 
1178
  fireResetQueueEvent : function()
1179
  {
1180
    this.fireEvent('resetqueue', this);
1181
  },
1182
 
1183
  uploadFile : function(record)
1184
  {
1185
    Ext.Ajax.request({
1186
      url : this.url,
1187
      params : this.base_params || this.baseParams || this.params,
1188
      method : 'POST',
1189
      form : this.form,
1190
      isUpload : true,
1191
      success : this.onAjaxSuccess,
1192
      failure : this.onAjaxFailure,
1193
      scope : this,
1194
      record: record
1195
    });
1196
  },
1197
 
1198
  fireFileUploadStartEvent : function(record)
1199
  {
1200
    this.fireEvent('fileuploadstart', this, record.get('filename'));
1201
  },
1202
 
1203
  updateRecordState : function(data)
1204
  {
1205
    if ('success' in data.response && data.response.success) {
1206
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FINISHED);
1207
      data.record.set(
1208
        'note', data.response.message || data.response.error || this.i18n.note_upload_success
1209
      );
1210
    }
1211
    else {
1212
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
1213
      data.record.set(
1214
        'note', data.response.message || data.response.error || this.i18n.note_upload_error
1215
      );
1216
    }
1217
 
1218
    data.record.commit();
1219
  },
1220
 
1221
  fireUploadSuccessEvent : function(data)
1222
  {
1223
    this.fireEvent('uploadsuccess', this, data.record.get('filename'), data.response);
1224
  },
1225
 
1226
  fireUploadErrorEvent : function(data)
1227
  {
1228
    this.fireEvent('uploaderror', this, data.record.get('filename'), data.response);
1229
  },
1230
 
1231
  fireUploadFailedEvent : function(data)
1232
  {
1233
    this.fireEvent('uploadfailed', this, data.record.get('filename'));
1234
  },
1235
 
1236
  fireUploadCompleteEvent : function()
1237
  {
1238
    this.fireEvent('uploadcomplete', this);
1239
  },
1240
 
1241
  findUploadFrame : function()
1242
  {
1243
    this.upload_frame = Ext.getBody().child('iframe.x-hidden:last');
1244
  },
1245
 
1246
  resetUploadFrame : function()
1247
  {
1248
    this.upload_frame = null;
1249
  },
1250
 
1251
  removeUploadFrame : function()
1252
  {
1253
    if (this.upload_frame) {
1254
      this.upload_frame.removeAllListeners();
1255
      this.upload_frame.dom.src = 'about:blank';
1256
      this.upload_frame.remove();
1257
    }
1258
    this.upload_frame = null;
1259
  },
1260
 
1261
  abortUpload : function()
1262
  {
1263
    this.removeUploadFrame();
1264
 
1265
    var store = this.grid_panel.getStore();
1266
    var record = null;
1267
    store.each(function(r) {
1268
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
1269
        record = r;
1270
        return false;
1271
      }
1272
    });
1273
 
1274
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
1275
    record.set('note', this.i18n.note_aborted);
1276
    record.commit();
1277
  },
1278
 
1279
  fireUploadStopEvent : function()
1280
  {
1281
    this.fireEvent('uploadstop', this);
1282
  },
1283
 
1284
  repostHide : function()
1285
  {
1286
    this.fsa.postEvent('hide');
1287
  },
1288
 
1289
  flushEventQueue : function()
1290
  {
1291
    this.fsa.flushEventQueue();
1292
  },
1293
 
1294
  /**
1295
   * @access private
1296
   */
1297
  // -------------------------------------------------------------------------------------------- //
1298
  onWindowRender : function()
1299
  {
1300
    this.fsa.postEvent('window-render');
1301
  },
1302
 
1303
  onWindowBeforeHide : function()
1304
  {
1305
    return this.isUploading() ? this.getAllowCloseOnUpload() : true;
1306
  },
1307
 
1308
  onWindowHide : function()
1309
  {
1310
    this.fsa.postEvent('hide');
1311
  },
1312
 
1313
  onWindowDestroy : function()
1314
  {
1315
    this.fsa.postEvent('destroy');
1316
  },
1317
 
1318
  onGridRender : function()
1319
  {
1320
    this.fsa.postEvent('grid-render');
1321
  },
1322
 
1323
  onGridSelectionChange : function()
1324
  {
1325
    this.fsa.postEvent('grid-selection-change');
1326
  },
1327
 
1328
  onAddButtonFileSelected : function(btn)
1329
  {
1330
    this.fsa.postEvent('file-selected', btn);
1331
  },
1332
 
1333
  onUploadButtonClick : function()
1334
  {
1335
    if (this.is_uploading) {
1336
      this.fsa.postEvent('stop-upload');
1337
    }
1338
    else {
1339
      this.fsa.postEvent('start-upload');
1340
    }
1341
  },
1342
 
1343
  onRemoveButtonClick : function()
1344
  {
1345
    var selections = this.grid_panel.getSelectionModel().getSelections();
1346
    this.fsa.postEvent('remove-files', selections);
1347
  },
1348
 
1349
  onResetButtonClick : function()
1350
  {
1351
    this.fsa.postEvent('reset-queue');
1352
  },
1353
 
1354
  onCloseButtonClick : function()
1355
  {
1356
    this[this.closeAction].call(this);
1357
  },
1358
 
1359
  onAjaxSuccess : function(response, options)
1360
  {
1361
    var json_response = {
1362
      'success' : false,
1363
      'error' : this.i18n.note_upload_error
1364
    }
1365
    try {
1366
        var rt = response.responseText;
1367
        var filter = rt.match(/^<[^>]+>((?:.|\n)*)<\/[^>]+>$/);
1368
        if (filter) {
1369
            rt = filter[1];
1370
        }
1371
        json_response = Ext.util.JSON.decode(rt);
1372
    }
1373
    catch (e) {}
1374
 
1375
    var data = {
1376
      record: options.record,
1377
      response: json_response
1378
    }
1379
 
1380
    if ('success' in json_response && json_response.success) {
1381
      this.fsa.postEvent('file-upload-success', data);
1382
    }
1383
    else {
1384
      this.fsa.postEvent('file-upload-error', data);
1385
    }
1386
  },
1387
 
1388
  onAjaxFailure : function(response, options)
1389
  {
1390
    var data = {
1391
      record : options.record,
1392
      response : {
1393
        'success' : false,
1394
        'error' : this.i18n.note_upload_failed
1395
      }
1396
    }
1397
 
1398
    this.fsa.postEvent('file-upload-failed', data);
1399
  },
1400
 
1401
  /**
1402
   * @access public
1403
   */
1404
  // -------------------------------------------------------------------------------------------- //
1405
  startUpload : function()
1406
  {
1407
    this.fsa.postEvent('start-upload');
1408
  },
1409
 
1410
  stopUpload : function()
1411
  {
1412
    this.fsa.postEvent('stop-upload');
1413
  },
1414
 
1415
  getUrl : function()
1416
  {
1417
    return this.url;
1418
  },
1419
 
1420
  setUrl : function(url)
1421
  {
1422
    this.url = url;
1423
  },
1424
 
1425
  getBaseParams : function()
1426
  {
1427
    return this.base_params;
1428
  },
1429
 
1430
  setBaseParams : function(params)
1431
  {
1432
    this.base_params = params;
1433
  },
1434
 
1435
  getUploadAutostart : function()
1436
  {
1437
    return this.upload_autostart;
1438
  },
1439
 
1440
  setUploadAutostart : function(value)
1441
  {
1442
    this.upload_autostart = value;
1443
  },
1444
 
1445
  getAllowCloseOnUpload : function()
1446
  {
1447
    return this.allow_close_on_upload;
1448
  },
1449
 
1450
  setAllowCloseOnUpload : function(value)
1451
  {
1452
    this.allow_close_on_upload;
1453
  },
1454
 
1455
  getResetOnHide : function()
1456
  {
1457
    return this.reset_on_hide;
1458
  },
1459
 
1460
  setResetOnHide : function(value)
1461
  {
1462
    this.reset_on_hide = value;
1463
  },
1464
 
1465
  getPermittedExtensions : function()
1466
  {
1467
    return this.permitted_extensions;
1468
  },
1469
 
1470
  setPermittedExtensions : function(value)
1471
  {
1472
    this.permitted_extensions = value;
1473
  },
1474
 
1475
  isUploading : function()
1476
  {
1477
    return this.is_uploading;
1478
  },
1479
 
1480
  isNotEmptyQueue : function()
1481
  {
1482
    return this.grid_panel.getStore().getCount() > 0;
1483
  },
1484
 
1485
  getQueuedCount : function(count_processing)
1486
  {
1487
    var count = 0;
1488
    var store = this.grid_panel.getStore();
1489
    store.each(function(r) {
1490
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
1491
        count++;
1492
      }
1493
      if (count_processing && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
1494
        count++;
1495
      }
1496
    });
1497
    return count;
1498
  },
1499
 
1500
  hasUnuploadedFiles : function()
1501
  {
1502
    return this.getQueuedCount() > 0;
1503
  }
1504
});
1505
 
1506
// ---------------------------------------------------------------------------------------------- //
1507
 
1508
var p = Ext.ux.UploadDialog.Dialog.prototype;
1509
p.i18n = {
1510
  title: 'File upload dialog',
1511
  state_col_title: 'State',
1512
  state_col_width: 70,
1513
  filename_col_title: 'Filename',
1514
  filename_col_width: 230,
1515
  note_col_title: 'Note',
1516
  note_col_width: 150,
1517
  add_btn_text: 'Add',
1518
  add_btn_tip: 'Add file into upload queue.',
1519
  remove_btn_text: 'Remove',
1520
  remove_btn_tip: 'Remove file from upload queue.',
1521
  reset_btn_text: 'Reset',
1522
  reset_btn_tip: 'Reset queue.',
1523
  upload_btn_start_text: 'Upload',
1524
  upload_btn_stop_text: 'Abort',
1525
  upload_btn_start_tip: 'Upload queued files to the server.',
1526
  upload_btn_stop_tip: 'Stop upload.',
1527
  close_btn_text: 'Close',
1528
  close_btn_tip: 'Close the dialog.',
1529
  progress_waiting_text: 'Waiting...',
1530
  progress_uploading_text: 'Uploading: {0} of {1} files complete.',
1531
  error_msgbox_title: 'Error',
1532
  permitted_extensions_join_str: ',',
1533
  err_file_type_not_permitted: 'Selected file extension isn\'t permitted.<br/>Please select files with following extensions: {1}',
1534
  note_queued_to_upload: 'Queued for upload.',
1535
  note_processing: 'Uploading...',
1536
  note_upload_failed: 'Server is unavailable or internal server error occured.',
1537
  note_upload_success: 'OK.',
1538
  note_upload_error: 'Upload error.',
1539
  note_aborted: 'Aborted by user.'
1540
}