﻿/**
 * This namespace should be in another file but I dicided to put it here for consistancy.
 */
Ext.namespace('Ext.ux.Utils');

/**
 * This class implements event queue behaviour.
 *
 * @class Ext.ux.Utils.EventQueue
 * @param function  handler  Event handler.
 * @param object    scope    Handler scope.
 */
Ext.ux.Utils.EventQueue = function(handler, scope)
{
  if (!handler) {
    throw 'Handler is required.';
  }
  this.handler = handler;
  this.scope = scope || window;
  this.queue = [];
  this.is_processing = false;
  
  /**
   * Posts event into the queue.
   * 
   * @access public
   * @param mixed event Event identificator.
   * @param mixed data  Event data.
   */
  this.postEvent = function(event, data)
  {
    data = data || null;
    this.queue.push({event: event, data: data});
    if (!this.is_processing) {
      this.process();
    }
  }
  
  this.flushEventQueue = function()
  {
    this.queue = [];
  },
  
  /**
   * @access private
   */
  this.process = function()
  {
    while (this.queue.length > 0) {
      this.is_processing = true;
      var event_data = this.queue.shift();
      this.handler.call(this.scope, event_data.event, event_data.data);
    }
    this.is_processing = false;
  }
}

/**
 * This class implements Mili's finite state automata behaviour.
 *  
 *  Transition / output table format:
 *  {
 *    'state_1' : {
 *      'event_1' : [
 *        {
 *          p|predicate: function,    // Transition predicate, optional, default to true.
 *                                    // If array then conjunction will be applyed to the operands.
 *                                    // Predicate signature is (data, event, this).
 *          a|action: function|array, // Transition action, optional, default to Ext.emptyFn.
 *                                    // If array then methods will be called sequentially.
 *                                    // Action signature is (data, event, this).
 *          s|state: 'state_x',       // New state - transition destination, optional, default to 
 *                                    // current state.
 *          scope: object             // Predicate and action scope, optional, default to 
 *                                    // trans_table_scope or window.
 *        }
 *      ]
 *    },
 *
 *    'state_2' : {
 *      ...
 *    }
 *    ...
 *  }
 *
 *  @param  mixed initial_state Initial state.
 *  @param  object trans_table Transition / output table.
 *  @param  trans_table_scope Transition / output table's methods scope.
 */
Ext.ux.Utils.FSA = function(initial_state, trans_table, trans_table_scope)
{
  this.current_state = initial_state;
  this.trans_table = trans_table || {};
  this.trans_table_scope = trans_table_scope || window;
  Ext.ux.Utils.FSA.superclass.constructor.call(this, this.processEvent, this);
}

Ext.extend(Ext.ux.Utils.FSA, Ext.ux.Utils.EventQueue, {

  current_state : null,
  trans_table : null,  
  trans_table_scope : null,
  
  /**
   * Returns current state
   * 
   * @access public
   * @return mixed Current state.
   */
  state : function()
  {
    return this.current_state;
  },
  
  /**
   * @access public
   */
  processEvent : function(event, data)
  {
    var transitions = this.currentStateEventTransitions(event);
    if (!transitions) {
      throw "State '" + this.current_state + "' has no transition for event '" + event + "'.";
    }
    for (var i = 0, len = transitions.length; i < len; i++) {
      var transition = transitions[i];

      var predicate = transition.predicate || transition.p || true;
      var action = transition.action || transition.a || Ext.emptyFn;
      var new_state = transition.state || transition.s || this.current_state;
      var scope = transition.scope || this.trans_table_scope;
      
      if (this.computePredicate(predicate, scope, data, event)) {
        this.callAction(action, scope, data, event);
        this.current_state = new_state; 
        return;
      }
    }
    
    throw "State '" + this.current_state + "' has no transition for event '" + event + "' in current context";
  },
  
  /**
   * @access private
   */
  currentStateEventTransitions : function(event)
  {
    return this.trans_table[this.current_state] ? 
      this.trans_table[this.current_state][event] || false
      :
      false;
  },
  
  /**
   * @access private
   */
  computePredicate : function(predicate, scope, data, event)
  {
    var result = false; 
    
    switch (Ext.type(predicate)) {
     case 'function':
       result = predicate.call(scope, data, event, this);
       break;
     case 'array':
       result = true;
       for (var i = 0, len = predicate.length; result && (i < len); i++) {
         if (Ext.type(predicate[i]) == 'function') {
           result = predicate[i].call(scope, data, event, this);
         }
         else {
           throw [
             'Predicate: ',
             predicate[i],
             ' is not callable in "',
             this.current_state,
             '" state for event "',
             event
           ].join('');
         }
       }
       break;
     case 'boolean':
       result = predicate;
       break;
     default:
       throw [
         'Predicate: ',
         predicate,
         ' is not callable in "',
         this.current_state,
         '" state for event "',
         event
       ].join('');
    }
    return result;
  },
  
  /**
   * @access private
   */
  callAction : function(action, scope, data, event)
  {
    switch (Ext.type(action)) {
       case 'array':
       for (var i = 0, len = action.length; i < len; i++) {
         if (Ext.type(action[i]) == 'function') {
           action[i].call(scope, data, event, this);
         }
         else {
           throw [
             'Action: ',
             action[i],
             ' is not callable in "',
             this.current_state,
             '" state for event "',
             event
           ].join('');
         }
       }
         break;
     case 'function':
       action.call(scope, data, event, this);
       break;
     default:
       throw [
         'Action: ',
         action,
         ' is not callable in "',
         this.current_state,
         '" state for event "',
         event
       ].join('');
    }
  }
});

// ---------------------------------------------------------------------------------------------- //

/**
 * Ext.ux.UploadDialog namespace.
 */
Ext.namespace('Ext.ux.UploadDialog');

/**
 * File upload browse button.
 *
 * @class Ext.ux.UploadDialog.BrowseButton
 */ 
Ext.ux.UploadDialog.BrowseButton = Ext.extend(Ext.Button, 
{
  input_name : 'file',
  
  input_file : null,
  
  original_handler : null,
  
  original_scope : null,
  
  /**
   * @access private
   */
  initComponent : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.initComponent.call(this);
    this.original_handler = this.handler || null;
    this.original_scope = this.scope || window;
    this.handler = null;
    this.scope = null;
  },
  
  /**
   * @access private
   */
  onRender : function(ct, position)
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.onRender.call(this, ct, position);
    this.createInputFile();
  },
  
  /**
   * @access private
   */
  createInputFile : function()
  {
    var button_container = this.el.child('.x-btn-center');
    button_container.position('relative');
    this.input_file = Ext.DomHelper.append(
      button_container, 
      {
        tag: 'input',
        type: 'file',
        size: 1,
        name: this.input_name || Ext.id(this.el),
        style: 'position: absolute; display: block; border: none; cursor: pointer'
      },
      true
    );
    
    var button_box = button_container.getBox();
    this.input_file.setStyle('font-size', (button_box.width * 0.5) + 'px');

    var input_box = this.input_file.getBox();
    var adj = {x: 3, y: 3}
    if (Ext.isIE) {
      adj = {x: 0, y: 3}
    }
    
    this.input_file.setLeft(button_box.width - input_box.width + adj.x + 'px');
    this.input_file.setTop(button_box.height - input_box.height + adj.y + 'px');
    this.input_file.setOpacity(0.0);
        
    if (this.handleMouseEvents) {
      this.input_file.on('mouseover', this.onMouseOver, this);
        this.input_file.on('mousedown', this.onMouseDown, this);
    }
    
    if(this.tooltip){
      if(typeof this.tooltip == 'object'){
        Ext.QuickTips.register(Ext.apply({target: this.input_file}, this.tooltip));
      } 
      else {
        this.input_file.dom[this.tooltipType] = this.tooltip;
        }
      }
    
    this.input_file.on('change', this.onInputFileChange, this);
    this.input_file.on('click', function(e) { e.stopPropagation(); }); 
  },
  
  /**
   * @access public
   */
  detachInputFile : function(no_create)
  {
    var result = this.input_file;
    
    no_create = no_create || false;
    
    if (typeof this.tooltip == 'object') {
      Ext.QuickTips.unregister(this.input_file);
    }
    else {
      this.input_file.dom[this.tooltipType] = null;
    }
    this.input_file.removeAllListeners();
    this.input_file = null;
    
    if (!no_create) {
      this.createInputFile();
    }
    return result;
  },
  
  /**
   * @access public
   */
  getInputFile : function()
  {
    return this.input_file;
  },
  
  /**
   * @access public
   */
  disable : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.disable.call(this);  
    this.input_file.dom.disabled = true;
  },
  
  /**
   * @access public
   */
  enable : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.enable.call(this);
    this.input_file.dom.disabled = false;
  },
  
  /**
   * @access public
   */
  destroy : function()
  {
    var input_file = this.detachInputFile(true);
    input_file.remove();
    input_file = null;
    Ext.ux.UploadDialog.BrowseButton.superclass.destroy.call(this);      
  },
  
  /**
   * @access private
   */
  onInputFileChange : function()
  {
    if (this.original_handler) {
      this.original_handler.call(this.original_scope, this);
    }
  }  
});

/**
 * Toolbar file upload browse button.
 *
 * @class Ext.ux.UploadDialog.TBBrowseButton
 */
Ext.ux.UploadDialog.TBBrowseButton = Ext.extend(Ext.ux.UploadDialog.BrowseButton, 
{
  hideParent : true,

  onDestroy : function()
  {
    Ext.ux.UploadDialog.TBBrowseButton.superclass.onDestroy.call(this);
    if(this.container) {
      this.container.remove();
      }
  }
});

/**
 * Record type for dialogs grid.
 *
 * @class Ext.ux.UploadDialog.FileRecord 
 */
Ext.ux.UploadDialog.FileRecord = Ext.data.Record.create([
  {name: 'filename'},
  {name: 'state', type: 'int'},
  {name: 'note'},
  {name: 'input_element'}
]);

Ext.ux.UploadDialog.FileRecord.STATE_QUEUE = 0;
Ext.ux.UploadDialog.FileRecord.STATE_FINISHED = 1;
Ext.ux.UploadDialog.FileRecord.STATE_FAILED = 2;
Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING = 3;

/**
 * Dialog class.
 *
 * @class Ext.ux.UploadDialog.Dialog
 */
Ext.ux.UploadDialog.Dialog = function(config)
{
  var default_config = {
    border: false,
    width: 450,
    height: 300,
    minWidth: 450,
    minHeight: 300,
    plain: true,
    constrainHeader: true,
    draggable: true,
    closable: true,
    maximizable: false,
    minimizable: false,
    resizable: true,
    autoDestroy: true,
    closeAction: 'hide',
    title: this.i18n.title,
    cls: 'ext-ux-uploaddialog-dialog',
    // --------
    url: '',
    base_params: {},
    permitted_extensions: [],
    reset_on_hide: true,
    allow_close_on_upload: false,
    upload_autostart: false
  }
  config = Ext.applyIf(config || {}, default_config);
  config.layout = 'absolute';
  
  Ext.ux.UploadDialog.Dialog.superclass.constructor.call(this, config);
}

Ext.extend(Ext.ux.UploadDialog.Dialog, Ext.Window, {

  fsa : null,
  
  state_tpl : null,
  
  form : null,
  
  grid_panel : null,
  
  progress_bar : null,
  
  is_uploading : false,
  
  initial_queued_count : 0,
  
  upload_frame : null,
  
  /**
   * @access private
   */
  //--------------------------------------------------------------------------------------------- //
  initComponent : function()
  {
    Ext.ux.UploadDialog.Dialog.superclass.initComponent.call(this);
    
    // Setting automata protocol
    var tt = {
      // --------------
      'created' : {
      // --------------
        'window-render' : [
          {
            action: [this.createForm, this.createProgressBar, this.createGrid],
            state: 'rendering'
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'rendering' : {
      // --------------
        'grid-render' : [
          {
            action: [this.fillToolbar, this.updateToolbar],
            state: 'ready'
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'ready' : {
      // --------------
        'file-selected' : [
          {
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
            action: this.addFileToUploadQueue,
            state: 'adding-file'
          },
          {
            // If file is not permitted then do nothing.
          }
        ],
        'grid-selection-change' : [
          {
            action: this.updateToolbar
          }
        ],
        'remove-files' : [
          {
            action: [this.removeFiles, this.fireFileRemoveEvent]
          }
        ],
        'reset-queue' : [
          {
            action: [this.resetQueue, this.fireResetQueueEvent]
          }
        ],
        'start-upload' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.setUploadingFlag, this.saveInitialQueuedCount, this.updateToolbar, 
              this.updateProgressBar, this.prepareNextUploadTask, this.fireUploadStartEvent
            ],
            state: 'uploading'
          },
          {
            // Has nothing to upload, do nothing.
          }
        ],
        'stop-upload' : [
          {
            // We are not uploading, do nothing. Can be posted by user only at this state. 
          }
        ],
        'hide' : [
          {
            predicate: [this.isNotEmptyQueue, this.getResetOnHide],
            action: [this.resetQueue, this.fireResetQueueEvent]
          },
          {
            // Do nothing
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'adding-file' : {
      // --------------
        'file-added' : [
          {
            predicate: this.isUploading,
            action: [this.incInitialQueuedCount, this.updateProgressBar, this.fireFileAddEvent],
            state: 'uploading' 
          },
          {
            predicate: this.getUploadAutostart,
            action: [this.startUpload, this.fireFileAddEvent],
            state: 'ready'
          },
          {
            action: [this.updateToolbar, this.fireFileAddEvent],
            state: 'ready'
          }
        ]
      },
      // --------------
      'uploading' : {
      // --------------
        'file-selected' : [
          {
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
            action: this.addFileToUploadQueue,
            state: 'adding-file'
          },
          {
            // If file is not permitted then do nothing.
          }
        ],
        'grid-selection-change' : [
          {
            // Do nothing.
          }
        ],
        'start-upload' : [
          {
            // Can be posted only by user in this state. 
          }
        ],
        'stop-upload' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar, 
              this.updateProgressBar, this.fireUploadStopEvent
            ],
            state: 'ready'
          },
          {
            action: [
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar, 
              this.updateProgressBar, this.fireUploadStopEvent, this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-start' : [
          {
            action: [this.uploadFile, this.findUploadFrame, this.fireFileUploadStartEvent]
          }
        ],
        'file-upload-success' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadSuccessEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadSuccessEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-error' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadErrorEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadErrorEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-failed' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadFailedEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadFailedEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'hide' : [
          {
            predicate: this.getResetOnHide,
            action: [this.stopUpload, this.repostHide]
          },
          {
            // Do nothing.
          }
        ],
        'destroy' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadingFlag, this.abortUpload,
              this.fireUploadStopEvent, this.flushEventQueue
            ],
            state: 'destroyed'
          },
          {
            action: [
              this.resetUploadingFlag, this.abortUpload,
              this.fireUploadStopEvent, this.fireUploadCompleteEvent, this.flushEventQueue
            ], 
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'destroyed' : {
      // --------------
      }
    }
    this.fsa = new Ext.ux.Utils.FSA('created', tt, this);
    
    // Registering dialog events.
    this.addEvents({
      'filetest': true,
      'fileadd' : true,
      'fileremove' : true,
      'resetqueue' : true,
      'uploadsuccess' : true,
      'uploaderror' : true,
      'uploadfailed' : true,
      'uploadstart' : true,
      'uploadstop' : true,
      'uploadcomplete' : true,
      'fileuploadstart' : true
    });
    
    // Attaching to window events.
    this.on('render', this.onWindowRender, this);
    this.on('beforehide', this.onWindowBeforeHide, this);
    this.on('hide', this.onWindowHide, this);
    this.on('destroy', this.onWindowDestroy, this);
    
    // Compiling state template.
    this.state_tpl = new Ext.Template(
      "<div class='ext-ux-uploaddialog-state ext-ux-uploaddialog-state-{state}'>&#160;</div>"
    ).compile();
  },
  
  createForm : function()
  {
    this.form = Ext.DomHelper.append(this.body, {
      tag: 'form',
      method: 'post',
      action: this.url,
      style: 'position: absolute; left: -100px; top: -100px; width: 100px; height: 100px'
    });
  },
  
  createProgressBar : function()
  {
    this.progress_bar = this.add(
      new Ext.ProgressBar({
        x: 0,
        y: 0,
        anchor: '0',
        value: 0.0,
        text: this.i18n.progress_waiting_text
      })
    );
  },
  
  createGrid : function()
  {
    var store = new Ext.data.Store({
      proxy: new Ext.data.MemoryProxy([]),
      reader: new Ext.data.JsonReader({}, Ext.ux.UploadDialog.FileRecord),
      sortInfo: {field: 'state', direction: 'DESC'},
      pruneModifiedRecords: true
    });
    
    var cm = new Ext.grid.ColumnModel([
      {
        header: this.i18n.state_col_title,
        width: this.i18n.state_col_width,
        resizable: false,
        dataIndex: 'state',
        sortable: true,
        renderer: this.renderStateCell.createDelegate(this)
      },
      {
        header: this.i18n.filename_col_title,
        width: this.i18n.filename_col_width,
        dataIndex: 'filename',
        sortable: true,
        renderer: this.renderFilenameCell.createDelegate(this)
      },
      {
        header: this.i18n.note_col_title,
        width: this.i18n.note_col_width, 
        dataIndex: 'note',
        sortable: true,
        renderer: this.renderNoteCell.createDelegate(this)
      }
    ]);
  
      this.grid_panel = new Ext.grid.GridPanel({
      ds: store,
      cm: cm,
    
      x: 0,
      y: 22,
      anchor: '0 -22',
      border: true,
      
        viewConfig: {
        autoFill: true,
          forceFit: true
        },
      
      bbar : new Ext.Toolbar()
    });
    this.grid_panel.on('render', this.onGridRender, this);
    
    this.add(this.grid_panel);
    
    this.grid_panel.getSelectionModel().on('selectionchange', this.onGridSelectionChange, this);
  },
  
  fillToolbar : function()
  {
    var tb = this.grid_panel.getBottomToolbar();
    tb.x_buttons = {}
    
    tb.x_buttons.add = tb.addItem(new Ext.ux.UploadDialog.TBBrowseButton({
      text: this.i18n.add_btn_text,
      tooltip: this.i18n.add_btn_tip,
      iconCls: 'ext-ux-uploaddialog-addbtn',
      handler: this.onAddButtonFileSelected,
      scope: this
    }));
    
    tb.x_buttons.remove = tb.addButton({
      text: this.i18n.remove_btn_text,
      tooltip: this.i18n.remove_btn_tip,
      iconCls: 'ext-ux-uploaddialog-removebtn',
      handler: this.onRemoveButtonClick,
      scope: this
    });
    
    tb.x_buttons.reset = tb.addButton({
      text: this.i18n.reset_btn_text,
      tooltip: this.i18n.reset_btn_tip,
      iconCls: 'ext-ux-uploaddialog-resetbtn',
      handler: this.onResetButtonClick,
      scope: this
    });
    
    tb.add('-');
    
    tb.x_buttons.upload = tb.addButton({
      text: this.i18n.upload_btn_start_text,
      tooltip: this.i18n.upload_btn_start_tip,
      iconCls: 'ext-ux-uploaddialog-uploadstartbtn',
      handler: this.onUploadButtonClick,
      scope: this
    });
    
    tb.add('-');
    
    tb.x_buttons.indicator = tb.addItem(
      new Ext.Toolbar.Item(
        Ext.DomHelper.append(tb.getEl(), {
          tag: 'div',
          cls: 'ext-ux-uploaddialog-indicator-stoped',
          html: '&#160'
        })
      )
    );
    
    tb.add('->');
    
    tb.x_buttons.close = tb.addButton({
      text: this.i18n.close_btn_text,
      tooltip: this.i18n.close_btn_tip,
      handler: this.onCloseButtonClick,
      scope: this
    });
  },
  
  renderStateCell : function(data, cell, record, row_index, column_index, store)
  {
    return this.state_tpl.apply({state: data});
  },
  
  renderFilenameCell : function(data, cell, record, row_index, column_index, store)
  {
    var view = this.grid_panel.getView();
    var f = function() {
      try {
        Ext.fly(
          view.getCell(row_index, column_index)
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
      }
      catch (e)
      {}
    }
    f.defer(1000);
    return data;
  },
  
  renderNoteCell : function(data, cell, record, row_index, column_index, store)
  {
    var view = this.grid_panel.getView();
    var f = function() {
      try {
        Ext.fly(
          view.getCell(row_index, column_index)
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
      }
      catch (e)
      {}
      }
    f.defer(1000);
    return data;
  },
  
  getFileExtension : function(filename)
  {
    var result = null;
    var parts = filename.split('.');
    if (parts.length > 1) {
      result = parts.pop();
    }
    return result;
  },
  
  isPermittedFileType : function(filename)
  {
    var result = true;
    if (this.permitted_extensions.length > 0) {
      result = this.permitted_extensions.indexOf(this.getFileExtension(filename)) != -1;
    }
    return result;
  },

  isPermittedFile : function(browse_btn)
  {
    var result = false;
    var filename = browse_btn.getInputFile().dom.value;
    
    if (this.isPermittedFileType(filename)) {
      result = true;
    }
    else {
      Ext.Msg.alert(
        this.i18n.error_msgbox_title, 
        String.format(
          this.i18n.err_file_type_not_permitted,
          filename,
          this.permitted_extensions.join(this.i18n.permitted_extensions_join_str)
        )
      );
      result = false;
    }
    
    return result;
  },
  
  fireFileTestEvent : function(browse_btn)
  {
    return this.fireEvent('filetest', this, browse_btn.getInputFile().dom.value) !== false;
  },
  
  addFileToUploadQueue : function(browse_btn)
  {
    var input_file = browse_btn.detachInputFile();
    
    input_file.appendTo(this.form);
    input_file.setStyle('width', '100px');
    input_file.dom.disabled = true;
    
    var store = this.grid_panel.getStore();
    store.add(
      new Ext.ux.UploadDialog.FileRecord({
          state: Ext.ux.UploadDialog.FileRecord.STATE_QUEUE,
          filename: input_file.dom.value,
          note: this.i18n.note_queued_to_upload,
          input_element: input_file
        })
      );
    this.fsa.postEvent('file-added', input_file.dom.value);
  },
  
  fireFileAddEvent : function(filename)
  {
    this.fireEvent('fileadd', this, filename);
  },
  
  updateProgressBar : function()
  {
    if (this.is_uploading) {
      var queued = this.getQueuedCount(true);
      var value = 1 - queued / this.initial_queued_count;
      this.progress_bar.updateProgress(
        value,
        String.format(
          this.i18n.progress_uploading_text, 
          this.initial_queued_count - queued,
          this.initial_queued_count
        )
      );
    }
    else {
      this.progress_bar.updateProgress(0, this.i18n.progress_waiting_text);
    }
  },
  
  updateToolbar : function()
  {
    var tb = this.grid_panel.getBottomToolbar();
    if (this.is_uploading) {
      tb.x_buttons.remove.disable();
      tb.x_buttons.reset.disable();
      tb.x_buttons.upload.enable();
      if (!this.getAllowCloseOnUpload()) {
        tb.x_buttons.close.disable();
      }
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
        'ext-ux-uploaddialog-indicator-stoped',
        'ext-ux-uploaddialog-indicator-processing'
      );
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstopbtn');
      tb.x_buttons.upload.setText(this.i18n.upload_btn_stop_text);
      tb.x_buttons.upload.getEl()
        .child(tb.x_buttons.upload.buttonSelector)
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_stop_tip;
    }
    else {
      tb.x_buttons.remove.enable();
      tb.x_buttons.reset.enable();
      tb.x_buttons.close.enable();
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
        'ext-ux-uploaddialog-indicator-processing',
        'ext-ux-uploaddialog-indicator-stoped'
      );
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstartbtn');
      tb.x_buttons.upload.setText(this.i18n.upload_btn_start_text);
      tb.x_buttons.upload.getEl()
        .child(tb.x_buttons.upload.buttonSelector)
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_start_tip;
      
      if (this.getQueuedCount() > 0) {
        tb.x_buttons.upload.enable();
      }
      else {
        tb.x_buttons.upload.disable();      
      }
      
      if (this.grid_panel.getSelectionModel().hasSelection()) {
        tb.x_buttons.remove.enable();
      }
      else {
        tb.x_buttons.remove.disable();
      }
      
      if (this.grid_panel.getStore().getCount() > 0) {
        tb.x_buttons.reset.enable();
      }
      else {
        tb.x_buttons.reset.disable();
      }
    }
  },
  
  saveInitialQueuedCount : function()
  {
    this.initial_queued_count = this.getQueuedCount();
  },
  
  incInitialQueuedCount : function()
  {
    this.initial_queued_count++;
  },
  
  setUploadingFlag : function()
  {
    this.is_uploading = true;
  }, 
  
  resetUploadingFlag : function()
  {
    this.is_uploading = false;
  },

  prepareNextUploadTask : function()
  {
    // Searching for first unuploaded file.
    var store = this.grid_panel.getStore();
    var record = null;
    
    store.each(function(r) {
      if (!record && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
        record = r;
      }
      else {
        r.get('input_element').dom.disabled = true;
      }
    });
    
    record.get('input_element').dom.disabled = false;
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING);
    record.set('note', this.i18n.note_processing);
    record.commit();
    
    this.fsa.postEvent('file-upload-start', record);
  },
   
  fireUploadStartEvent : function()
  {
    this.fireEvent('uploadstart', this);
  },
  
  removeFiles : function(file_records)
  {
    var store = this.grid_panel.getStore();
    for (var i = 0, len = file_records.length; i < len; i++) {
      var r = file_records[i];
      r.get('input_element').remove();
      store.remove(r);
    }
  },
  
  fireFileRemoveEvent : function(file_records)
  {
    for (var i = 0, len = file_records.length; i < len; i++) {
      this.fireEvent('fileremove', this, file_records[i].get('filename'));
    }
  },
  
  resetQueue : function()
  {
    var store = this.grid_panel.getStore();
    store.each(
      function(r) {
        r.get('input_element').remove();
      }
    );
    store.removeAll();
  },
  
  fireResetQueueEvent : function()
  {
    this.fireEvent('resetqueue', this);
  },
  
  uploadFile : function(record)
  {
  	
    Ext.Ajax.request({
      url : this.url,
      params : this.base_params || this.baseParams || this.params,
      method : 'POST',
      form : this.form,
      isUpload : true,
      success : this.onAjaxSuccess,
      failure : this.onAjaxFailure,
      scope : this,
      record: record
    });
  },
   
  fireFileUploadStartEvent : function(record)
  {
    this.fireEvent('fileuploadstart', this, record.get('filename'));
  },
  
  updateRecordState : function(data)
  {
    if ('success' in data.response && data.response.success) {
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FINISHED);
      data.record.set(
        'note', data.response.message || data.response.error || this.i18n.note_upload_success
      );
    }
    else {
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
      data.record.set(
        'note', data.response.message || data.response.error || this.i18n.note_upload_error
      );
    }
    
    data.record.commit();
  },
  
  fireUploadSuccessEvent : function(data)
  {
    this.fireEvent('uploadsuccess', this, data.record.get('filename'), data.response);
  },
  
  fireUploadErrorEvent : function(data)
  {
    this.fireEvent('uploaderror', this, data.record.get('filename'), data.response);
  },
  
  fireUploadFailedEvent : function(data)
  {
    this.fireEvent('uploadfailed', this, data.record.get('filename'));
  },
  
  fireUploadCompleteEvent : function()
  {
    this.fireEvent('uploadcomplete', this);
  },
  
  findUploadFrame : function() 
  {
    this.upload_frame = Ext.getBody().child('iframe.x-hidden:last');
  },
  
  resetUploadFrame : function()
  {
    this.upload_frame = null;
  },
  
  removeUploadFrame : function()
  {
    if (this.upload_frame) {
      this.upload_frame.removeAllListeners();
      this.upload_frame.dom.src = 'about:blank';
      this.upload_frame.remove();
    }
    this.upload_frame = null;
  },
  
  abortUpload : function()
  {
    this.removeUploadFrame();
    
    var store = this.grid_panel.getStore();
    var record = null;
    store.each(function(r) {
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
        record = r;
        return false;
      }
    });
    
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
    record.set('note', this.i18n.note_aborted);
    record.commit();
  },
  
  fireUploadStopEvent : function()
  {
    this.fireEvent('uploadstop', this);
  },
  
  repostHide : function()
  {
    this.fsa.postEvent('hide');
  },
  
  flushEventQueue : function()
  {
    this.fsa.flushEventQueue();
  },
  
  /**
   * @access private
   */
  // -------------------------------------------------------------------------------------------- //
  onWindowRender : function()
  {
    this.fsa.postEvent('window-render');
  },
  
  onWindowBeforeHide : function()
  {
    return this.isUploading() ? this.getAllowCloseOnUpload() : true;
  },
  
  onWindowHide : function()
  {
    this.fsa.postEvent('hide');
  },
  
  onWindowDestroy : function()
  {
    this.fsa.postEvent('destroy');
  },
  
  onGridRender : function()
  {
    this.fsa.postEvent('grid-render');
  },
  
  onGridSelectionChange : function()
  {
    this.fsa.postEvent('grid-selection-change');
  },
  
  onAddButtonFileSelected : function(btn)
  {
    this.fsa.postEvent('file-selected', btn);
  },
  
  onUploadButtonClick : function()
  {
    if (this.is_uploading) {
      this.fsa.postEvent('stop-upload');
    }
    else {
      this.fsa.postEvent('start-upload');
    }
  },
  
  onRemoveButtonClick : function()
  {
    var selections = this.grid_panel.getSelectionModel().getSelections();
    this.fsa.postEvent('remove-files', selections);
  },
  
  onResetButtonClick : function()
  {
    this.fsa.postEvent('reset-queue');
  },
  
  onCloseButtonClick : function()
  {
    this[this.closeAction].call(this);
  },
  
  onAjaxSuccess : function(response, options)
  {
    var json_response = {
      'success' : false,
      'error' : this.i18n.note_upload_error
    }
    try { var json_response = Ext.util.JSON.decode(response.responseText); } catch (e) {}
    
    var data = {
      record: options.record,
      response: json_response
    }
    if ('success' in json_response && json_response.success) {
      this.fsa.postEvent('file-upload-success', data);
    }
    else {
      this.fsa.postEvent('file-upload-error', data);
    }
  },
  
  onAjaxFailure : function(response, options)
  {
    var data = {
      record : options.record,
      response : {
        'success' : false,
        'error' : this.i18n.note_upload_failed
      }
    }
		
    this.fsa.postEvent('file-upload-failed', data);
  },
  
  /**
   * @access public
   */
  // -------------------------------------------------------------------------------------------- //
  startUpload : function()
  {
    this.fsa.postEvent('start-upload');
  },
  
  stopUpload : function()
  {
    this.fsa.postEvent('stop-upload');
  },
  
  getUrl : function()
  {
    return this.url;
  },
  
  setUrl : function(url)
  {
    this.url = url;
  },
  
  getBaseParams : function()
  {
    return this.base_params;
  },
  
  setBaseParams : function(params)
  {
    this.base_params = params;
  },
  
  getUploadAutostart : function()
  {
    return this.upload_autostart;
  },
  
  setUploadAutostart : function(value)
  {
    this.upload_autostart = value;
  },
  
  getAllowCloseOnUpload : function()
  {
    return this.allow_close_on_upload;
  },
  
  setAllowCloseOnUpload : function(value)
  {
    this.allow_close_on_upload;
  },
  
  getResetOnHide : function()
  {
    return this.reset_on_hide;
  },
  
  setResetOnHide : function(value)
  {
    this.reset_on_hide = value;
  },
  
  getPermittedExtensions : function()
  {
    return this.permitted_extensions;
  },
  
  setPermittedExtensions : function(value)
  {
    this.permitted_extensions = value;
  },
  
  isUploading : function()
  {
    return this.is_uploading;
  },
  
  isNotEmptyQueue : function()
  {
    return this.grid_panel.getStore().getCount() > 0;
  },
  
  getQueuedCount : function(count_processing)
  {
    var count = 0;
    var store = this.grid_panel.getStore();
    store.each(function(r) {
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
        count++;
      }
      if (count_processing && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
        count++;
      }
    });
    return count;
  },
  
  hasUnuploadedFiles : function()
  {
    return this.getQueuedCount() > 0;
  }
});

// ---------------------------------------------------------------------------------------------- //

var p = Ext.ux.UploadDialog.Dialog.prototype;
p.i18n = {
  title: 'File upload dialog',
  state_col_title: 'State',
  state_col_width: 70,
  filename_col_title: 'Filename',
  filename_col_width: 230,  
  note_col_title: 'Note',
  note_col_width: 150,
  add_btn_text: 'Add',
  add_btn_tip: 'Add file into upload queue.',
  remove_btn_text: 'Remove',
  remove_btn_tip: 'Remove file from upload queue.',
  reset_btn_text: 'Reset',
  reset_btn_tip: 'Reset queue.',
  upload_btn_start_text: 'Upload',
  upload_btn_stop_text: 'Abort',
  upload_btn_start_tip: 'Upload queued files to the server.',
  upload_btn_stop_tip: 'Stop upload.',
  close_btn_text: 'Close',
  close_btn_tip: 'Close the dialog.',
  progress_waiting_text: 'Waiting...',
  progress_uploading_text: 'Uploading: {0} of {1} files complete.',
  error_msgbox_title: 'Error',
  permitted_extensions_join_str: ',',
  err_file_type_not_permitted: 'Selected file extension isn\'t permitted.<br/>Please select files with following extensions: {1}',
  note_queued_to_upload: 'Queued for upload.',
  note_processing: 'Uploading...',
  note_upload_failed: 'Server is unavailable or internal server error occured.',
  note_upload_success: 'OK.',
  note_upload_error: 'Upload error.',
  note_aborted: 'Aborted by user.'
}

p.i18n = {
  title: 'Upload súborov na server',
  state_col_title: 'Stav',
  state_col_width: 70,
  filename_col_title: 'Názov súboru',
  filename_col_width: 230,  
  note_col_title: 'Poznámka',
  note_col_width: 150,
  add_btn_text: 'Pridať',
  add_btn_tip: 'Pridať súbor do fronty na nahrávanie.',
  remove_btn_text: 'Odstrániť',
  remove_btn_tip: 'Odstrániť súbor z fronty na nahrávanie.',
  reset_btn_text: 'Reset',
  reset_btn_tip: 'Resetovať frontu.',
  upload_btn_start_text: 'Nahrať na server',
  upload_btn_stop_text: 'Zrušiť',
  upload_btn_start_tip: 'Nahrávanie fronty na server.',
  upload_btn_stop_tip: 'Stop nahrávaniu.',
  close_btn_text: 'Zatvoriť',
  close_btn_tip: 'Zatvoriť okno.',
  progress_waiting_text: 'Čakám...',
  progress_uploading_text: 'Nahrávam: {0} z {1} súborov nahratých.',
  error_msgbox_title: 'Chyba',
  permitted_extensions_join_str: ',',
  err_file_type_not_permitted: 'Prípona vybratého súboru nie je povolená.<br/>Prosím vyberte súbor s jednou z nasledujúcich prípon: {1}',
  note_queued_to_upload: 'Súbory čakajúce na nahratie na server.',
  note_processing: 'Nahrávam na server...',
  note_upload_failed: 'Server je nedostupný alebo nastala vnútorná chyba servera.',
  note_upload_success: 'OK.',
  note_upload_error: 'Chyba nahrávania.',
  note_aborted: 'Prerušené užívateľom.'
}

/**
 * Based on code found at http://extjs.com/forum/showthread.php?t=5106
 * 
 * Modified by Merijn Schering <mschering@intermesh.nl>
 * 
 * Changes: 
 *  -Handles value better. Uses value config property as start value. 
 *  -Removed changed trigger image because it didn't handle state.
 *     -Added colors config property so you can overide the default color palette * 
 * 
 * @class GO.form.ColorField
 * @extends Ext.form.TriggerField
 * Provides a very simple color form field with a ColorMenu dropdown.
 * Values are stored as a six-character hex value without the '#'.
 * I.e. 'ffffff'
 * @constructor
 * Create a new ColorField
 * <br />Example:
 * <pre><code>
var cf = new Ext.form.ColorField({
    fieldLabel: 'Color',
    hiddenName:'pref_sales',
    showHexValue:true
});
</code></pre>
 * @param {Object} config
 */


Ext.form.ColorField =  Ext.extend(function(config){
    
  this.menu = new Ext.menu.ColorMenu();
    this.menu.palette.on('select', this.handleSelect, this );

  this.menu.on(Ext.apply({}, this.menuListeners, {
      scope:this
  }));
  
  if(config.colors)
  {
      this.menu.palette.colors=config.colors;
  }
  
  Ext.form.ColorField.superclass.constructor.call(this, config);
  
},Ext.form.TriggerField,  {
    
    /**
     * @cfg {Boolean} showHexValue
     * True to display the HTML Hexidecimal Color Value in the field
     * so it is manually editable.
     */
    showHexValue : false,
    
    /**
       * @cfg {String} triggerClass
       * An additional CSS class used to style the trigger button.  The trigger will always get the
       * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-color-trigger'
       * which displays a calendar icon).
       
    triggerClass : 'go-form-color-trigger',
    * */
    
    /**
     * @cfg {String/Object} autoCreate
     * A DomHelper element spec, or true for a default element spec (defaults to
     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
     */
    // private
    defaultAutoCreate : {tag: "input", type: "text", size: "1", autocomplete: "off", maxlength:"6"},
    
    /**
     * @cfg {String} lengthText
     * A string to be displayed when the length of the input field is
     * not 3 or 6, i.e. 'fff' or 'ffccff'.
     */
    lengthText: "Color hex values must be either 3 or 6 characters.",
    
    //text to use if blank and allowBlank is false
    blankText: "Must have a hexidecimal value in the format ABCDEF.",
    
    /**
     * @cfg {String} color
     * A string hex value to be used as the default color.  Defaults
     * to 'FFFFFF' (white).
     */
    //defaultColor: 'FFFFFF',
    
    maskRe: /[a-f0-9]/i,
    // These regexes limit input and validation to hex values
    regex: /[a-f0-9]/i,

    //private
    curColor: 'ffffff',
    
    // private
    validateValue : function(value){
        if(!this.showHexValue) {
            return true;
        }
        if(value.length<1) {
            this.el.setStyle({
                'background-color':'#' + this.defaultColor
            });
            if(!this.allowBlank) {
                this.markInvalid(String.format(this.blankText, value));
                return false
            }
            return true;
        }
        if(value.length!=3 && value.length!=6 ) {
            this.markInvalid(String.format(this.lengthText, value));
            return false;
        }
        this.setColor(value);
        return true;
    },

    // private
    validateBlur : function(){
        return !this.menu || !this.menu.isVisible();
    },
    
    // Manually apply the invalid line image since the background
    // was previously cleared so the color would show through.
    markInvalid : function( msg ) {
        Ext.form.ColorField.superclass.markInvalid.call(this, msg);
        this.el.setStyle({
            'background-image': 'url(../lib/resources/images/default/grid/invalid_line.gif)'
        });
    },

  /**
   * Returns the current color value of the color field
   * @return {String} value The hexidecimal color value
  
  getValue : function(){
        return this.curValue || this.defaultValue || "FFFFFF";
  }, */

  /**
   * Sets the value of the color field.  Format as hex value 'FFFFFF'
   * without the '#'.
   * @param {String} hex The color value
   */
  setValue : function(hex){
        Ext.form.ColorField.superclass.setValue.call(this, hex);
        this.setColor(hex);
  },
    
    /**
     * Sets the current color and changes the background.
     * Does *not* change the value of the field.
     * @param {String} hex The color value.
     */
    setColor : function(hex) {
        this.curColor = hex;
        
        this.el.setStyle( {
            'background-color': '#' + hex,
            'background-image': 'none'
        });
        if(!this.showHexValue) {
            this.el.setStyle({
                'text-indent': '-100px'
            });
            if(Ext.isIE) {
                this.el.setStyle({
                    'margin-left': '100px'
                });
            }
        }
    },

  // private
  menuListeners : {
      select: function(m, d){
          this.setValue(d);
      },
      show : function(){ // retain focus styling
          this.onFocus();
      },
      hide : function(){
          this.focus();
          var ml = this.menuListeners;
          this.menu.un("select", ml.select,  this);
          this.menu.un("show", ml.show,  this);
          this.menu.un("hide", ml.hide,  this);
      }
  },
    
    //private
    handleSelect : function(palette, selColor) {
        this.setValue(selColor);
    },

  // private
  // Implements the default empty TriggerField.onTriggerClick function to display the ColorPicker
  onTriggerClick : function(){
      if(this.disabled){
          return;
      }
      
      this.menu.show(this.el, "tl-bl?");
  }
});

Ext.reg('colorfield', Ext.form.ColorField); 

// --------------------------------------------------------
// ImageChooser
// --------------------------------------------------------

var current_directory = '';
var selectedNode = '';

function do_delete_file(deleteurl){
	Ext.MessageBox.confirm('Potvrdenie', 'Ste si istý, že chcete vymazať tento súbor?', function(reponse) {
	if (reponse == "yes") {
		var connection = new Ext.data.Connection().request({
		url:deleteurl,
		method: "POST",
		params: {directory:current_directory,file:selectedNode.id},
		success: function(o){
			Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{dir:current_directory,method:'POST'}});
		},
		failure: function(o){
			Ext.Msg.show({title:"Chyba",msg:response.message,buttons:Ext.Msg.OK});
		}
		});
	}
});
}

function do_delete_dir(deldirurl){
	Ext.MessageBox.confirm('Potvrdenie', 'Ste si istý, že chcete vymazať tento adresár?', function(reponse) {
	if (reponse == "yes") {
		var connection = new Ext.data.Connection().request({
		url:deldirurl,
		method: "POST",
		params: {directory:current_directory},
		success: function(o){
		
			var response = Ext.util.JSON.decode(o.responseText);
			Ext.getCmp('choosertree').getRootNode().reload();
			Ext.getCmp('choosertree').getRootNode().expand();
			Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{dir:current_directory,method:'POST'}});
		},
		failure: function(o){
			Ext.Msg.show({title:"Chyba",msg:response.message,buttons:Ext.Msg.OK});
		}
		});
	}
});
}

function do_new_directory(newdirurl,directory) {
			Ext.MessageBox.prompt('Nový adresár', 'Názov nového adresára', function(reponse, text) {
				if (reponse == "ok") {
					var connection = new Ext.data.Connection().request({
						url: newdirurl,
						method: "POST",
						params: {action: "new_directory", directory: directory, new_directory: text},
						success: function(o){
							var response = Ext.util.JSON.decode(o.responseText);
								Ext.getCmp('choosertree').getRootNode().reload();
								Ext.getCmp('choosertree').getRootNode().expand();
						},
						failure: function(o) {
							var response = Ext.util.JSON.decode(o.responseText);
							// Set a status bar message
							Ext.getCmp('status_bar').setStatus({
								text: response.message,
								iconCls: 'save_warning_icon',
								clear: true
							});
						}
					});
				}
			});
}

var ImageChooser = function(config){
	this.config = config;
}



ImageChooser.prototype = {
    // cache data by image name for easy lookup
    lookup : {},
    
	show : function(el, callback){
		if(!this.win){
			this.initTemplates();
			this.rootel=el;
			this.store = new Ext.data.JsonStore({
			   	url:this.config.url,
			    root: 'images',
			    fields: [
			        'name', 'basefold','url','thumb',
			        {name:'size', type: 'float'},
			        {name:'lastmod', type:'date', dateFormat:'timestamp'}
			    ],
			    listeners: {
			    	'load': {fn:function(){ this.view.select(0); }, scope:this, single:true}
			    }
			});
			this.store.load();
			
			var formatSize = function(data){
		        if(data.size < 1024) {
		            return data.size + " bytes";
		        } else {
		            return (Math.round(((data.size*10) / 1024))/10) + " KB";
		        }
		    };
			
			var formatData = function(data){
		    	data.shortName = data.name.ellipse(15);
		    	data.sizeString = formatSize(data);
		    	data.dateString = new Date(data.lastmod).format("m/d/Y g:i a");
		    	this.lookup[data.name] = data;
		    	return data;
		    };
			var uploadurl = this.config.uploadurl;  
		  var deleteurl = this.config.deleteurl;
		  var newdirurl = this.config.newdirurl;
		  var renamedirurl = this.config.renamedirurl;
		  var deldirurl = this.config.deldirurl;
		  
		  var tree_context_menu = new Ext.menu.Menu({
		  	id: 'tree_context_menu',
		  	items: [{
					text: 'Nový adresár',
					iconCls: 'new_directory_button',
					handler: function(){do_new_directory(newdirurl,tree_context_menu.node.attributes.url);}
				},
				{
				//	text: 'Premenovať adresár',
				//	iconCls: 'rename_directory_button',
				//	handler: function(){console.log("rename directory");}
				//},{
					text: 'Vymazať adresár',
					iconCls: 'delete_directory_button',
					handler: function(){do_delete_dir(deldirurl);}
				}]
		  });
		  
		  var file_context_menu = new Ext.menu.Menu({
		  	id: 'file_context_menu',
		  	items: [{
		  			text: 'Vymazať súbor',
		  			iconCls: 'delete_button',
		  			handler: function(){do_delete_file(deleteurl);}
		  		}]
		  
		  });
		  
		  this.view = new Ext.DataView({
				tpl: this.thumbTemplate,
				singleSelect: true,
				overClass:'x-view-over',
				itemSelector: 'div.thumb-wrap',
				emptyText : '<div style="padding:10px;">No files match the specified filter</div>',
				store: this.store,
				listeners: {
					'selectionchange': {fn:this.showDetails, scope:this, buffer:100},
					'dblclick'       : {fn:this.doCallback, scope:this},
					'loadexception'  : {fn:this.onLoadException, scope:this},
					'beforeselect'   : {fn:function(view){
				        return view.store.getRange().length > 0;
				    }},
				  'contextmenu'		 : function(view,index,node,e){e.stopEvent();var coords = e.getXY();view.select(index);file_context_menu.showAt([coords[0], coords[1]]);}
				},
				prepareData: formatData.createDelegate(this)
			});
		  
		  
		  this.tree = new Ext.tree.TreePanel({
				autoScroll: true,
				id:'choosertree',
				animate: true,
				containerScroll: true,
				border: false,
				autoHeight:true,
				enableDD: false,
				ddGroup : 'fileMove',
				loader: new Ext.tree.TreeLoader({
					dataUrl: this.config.dirurl
				}),
				root: new Ext.tree.AsyncTreeNode({
					text: 'Files',
					draggable: false,
					id: 'source',
					expanded: true
				}),
				listeners: {
					'click': function (node,e){
											current_directory = node.attributes.url;
											if(current_directory!='') {
												Ext.getCmp('upload_button').enable();
											} else {
												Ext.getCmp('upload_button').disable();
											}
											Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{dir:current_directory,method:'POST'}});
										},
					'contextmenu': function(node,e){
											node.select();
											tree_context_menu.node = node;
											tree_context_menu.show(e.getTarget());
										}
				}
			});
		  new Ext.tree.TreeSorter(this.tree, {folderSort: true});
		  
		  
		  
			var cfg = {
		    	title: 'Vyberte si obrázok',
		    	id: 'img-chooser-dlg',
		    	layout: 'border',
				minWidth: 700,
				minHeight: 300,
				modal: true,
				closeAction: 'close',
				border: false,
				items:[{
					id: 'folder-panel',
					items: this.tree,tbar:[{}],
					region: 'west',
					split: true,
					width: 200,
					minWidth: 200,
					maxWidth: 450
				},{
					id: 'img-chooser-view',
					region: 'center',
					autoScroll: true,
					items: this.view,
                    tbar:[{
                    		id: 'upload_button',
                    		text: 'Nahrať na server',
                    		tooltip: 'Nahrať nový súbor na server',
                    		iconCls: 'upload_button',
                    		disabled: true,
                    		handler: function(){
                    			var upload_dialog = new Ext.ux.UploadDialog.Dialog({
															title: 'Upload Files',
															url: uploadurl,
															base_params: {action: 'upload', directory: current_directory},
															minWidth: 400,
															minHeight: 200,
															width: 400,
															height: 350,
															reset_on_hide: false,
															debug:true,
															allow_close_on_upload: false
														});
														upload_dialog.show('upload_button');
													upload_dialog.on("uploadcomplete", function() {
														Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{dir:current_directory,method:'POST'}});
													});
													}
                    		},{
                    			id: 'delete_button',
                    			text: 'Vymazať súbor',
                    			tooltip: 'Vymazať vybratý súbor',
                    			iconCls: 'delete_button',
                    			disabled: true,
                    			handler: function(){do_delete_file(deleteurl);}
                    		},{
                    	text: 'Filter:'
                    },{
                    	xtype: 'textfield',
                    	id: 'filter',
                    	selectOnFocus: true,
                    	width: 100,
                    	listeners: {
                    		'render': {fn:function(){
						    	Ext.getCmp('filter').getEl().on('keyup', function(){
						    		this.filter();
						    	}, this, {buffer:500});
                    		}, scope:this}
                    	}
                    }, ' ', '-', {
                    	text: 'Zoradiť podľa:'
                    }, {
                    	id: 'sortSelect',
                    	xtype: 'combo',
				        typeAhead: true,
				        triggerAction: 'all',
				        width: 100,
				        editable: false,
				        mode: 'local',
				        displayField: 'desc',
				        valueField: 'name',
				        lazyInit: false,
				        value: 'name',
				        store: new Ext.data.SimpleStore({
					        fields: ['name', 'desc'],
					        data : [['name', 'Názvu'],['size', 'Veľkosti súboru'],['lastmod', 'Poslednej zmeny']]
					    }),
					    listeners: {
							'select': {fn:this.sortImages, scope:this}
					    }
				    }]
				},{
					id: 'img-detail-panel',
					region: 'east',
					split: true,
					width: 150,
					minWidth: 150,
					maxWidth: 250
				}],
				buttons: [{
					id: 'ok-btn',
					text: 'OK',
					handler: this.doCallback,
					scope: this
				},{
					text: 'Cancel',
					handler: function(){ this.win.close(); },
					scope: this
				}],
				keys: {
					key: 27, // Esc key
					handler: function(){ this.win.close(); },
					scope: this
				}
			};
			Ext.apply(cfg, this.config);
		    this.win = new Ext.Window(cfg);
		}
		
		this.reset();
	  this.win.show(el);
		this.callback = callback;
		this.animateTarget = el;
		return "";
	},
	
	initTemplates : function(){
		this.thumbTemplate = new Ext.XTemplate(
			'<tpl for=".">',
				'<div class="thumb-wrap" id="{name}" name="{url}">',
				'<div class="thumb"><img src="{thumb}" title="{name}"></div>',
				'<span>{shortName}</span></div>',
			'</tpl>'
		);
		this.thumbTemplate.compile();
		
		this.detailsTemplate = new Ext.XTemplate(
			'<div class="details">',
				'<tpl for=".">',
					'<img src="{thumb}" style="width:120px;"><div class="details-info">',
					'<b>Názov:</b>',
					'<span>{name}</span>',
					'<b>Veľkosť:</b>',
					'<span>{sizeString}</span>',
					'<b>Posledná zmena:</b>',
					'<span>{dateString}</span></div>',
				'</tpl>',
			'</div>'
		);
		this.detailsTemplate.compile();
	},
	
	showDetails : function(){
	    var selNode = this.view.getSelectedNodes();
	    if(Ext.getCmp('img-detail-panel')!=undefined){
			    var detailEl = Ext.getCmp('img-detail-panel').body;
				if(selNode && selNode.length > 0){
					selNode = selNode[0];
					selectedNode = selNode;
					Ext.getCmp('ok-btn').enable();
					Ext.getCmp('delete_button').enable();
				    var data = this.lookup[selNode.id];
		            detailEl.hide();
		            this.detailsTemplate.overwrite(detailEl, data);
		            detailEl.slideIn('l', {stopFx:true,duration:.2});
				}else{
				    Ext.getCmp('ok-btn').disable();
				    Ext.getCmp('delete_button').disable();
				    detailEl.update('');
				}
		}
	},
	
	filter : function(){
		var filter = Ext.getCmp('filter');
		this.view.store.filter('name', filter.getValue());
		this.view.select(0);
	},
	
	sortImages : function(){
		var v = Ext.getCmp('sortSelect').getValue();
    	this.view.store.sort(v, v == 'name' ? 'asc' : 'desc');
    	this.view.select(0);
    },
	
	reset : function(){
		if(this.win.rendered){
			Ext.getCmp('filter').reset();
			this.view.getEl().dom.scrollTop = 0;
		}
	    this.view.store.clearFilter();
		this.view.select(0);
	},
	
	doCallback : function(){
		
    var selNode = this.view.getSelectedRecords()[0];
		var callback = this.callback;
		var lookup = this.lookup;
		var rootel=this.rootel;
		var iswin=this.win;
		var data = selNode.data.url;
		callback(data,rootel,iswin);
		this.win.destroy();
    },
	
	onLoadException : function(v,o){
	    this.view.getEl().update('<div style="padding:10px;">Error loading images.</div>'); 
	}
};

//***************************
//****** FILECHOOSER ********
//***************************
var FileChooser = function(config){
	this.config = config;
}

FileChooser.prototype = {
    // cache data by image name for easy lookup
    lookup : {},
    
	show : function(el, callback){
		if(!this.win){
			this.initTemplates();
			this.rootel=el;
			this.store = new Ext.data.JsonStore({
			   	url:this.config.url,
			    root: 'images',
			    fields: [
			        'name', 'basefold','url','thumb',
			        {name:'size', type: 'float'},
			        {name:'lastmod', type:'date', dateFormat:'timestamp'}
			    ],
			    listeners: {
			    	'load': {fn:function(){ this.view.select(0); }, scope:this, single:true}
			    }
			});
			this.store.load({params:{'allowedfiles':this.config.allowedfiles}});
		
			var formatSize = function(data){
		        if(data.size < 1024) {
		            return data.size + " bytes";
		        } else {
		            return (Math.round(((data.size*10) / 1024))/10) + " KB";
		        }
		    };
			
			var formatData = function(data){
		    	data.shortName = data.name.ellipse(15);
		    	data.sizeString = formatSize(data);
		    	data.dateString = new Date(data.lastmod).format("m/d/Y g:i a");
		    	this.lookup[data.name] = data;
		    	return data;
		    };
		  var allowedfiles = this.config.allowedfiles;
			var uploadurl = this.config.uploadurl;  
		  var deleteurl = this.config.deleteurl;
		  var newdirurl = this.config.newdirurl;
		  var renamedirurl = this.config.renamedirurl;
		  var deldirurl = this.config.deldirurl;
		  
		  var tree_context_menu = new Ext.menu.Menu({
		  	id: 'tree_context_menu',
		  	items: [{
					text: 'Nový adresár',
					iconCls: 'new_directory_button',
					handler: function(){do_new_directory(newdirurl,tree_context_menu.node.attributes.url);}
				},
				{
				//	text: 'Premenovať adresár',
				//	iconCls: 'rename_directory_button',
				//	handler: function(){console.log("rename directory");}
				//},{
					text: 'Vymazať adresár',
					iconCls: 'delete_directory_button',
					handler: function(){do_delete_dir(deldirurl);}
				}]
		  });
		  
		  var file_context_menu = new Ext.menu.Menu({
		  	id: 'file_context_menu',
		  	items: [{
		  			text: 'Vymazať súbor',
		  			iconCls: 'delete_button',
		  			handler: function(){do_delete_file(deleteurl);}
		  		}]
		  
		  });
		  
		  this.view = new Ext.DataView({
				tpl: this.thumbTemplate,
				singleSelect: true,
				overClass:'x-view-over',
				itemSelector: 'div.thumb-wrap',
				emptyText : '<div style="padding:10px;">Žiadne súbory zvoleného typu</div>',
				store: this.store,
				listeners: {
					'selectionchange': {fn:this.showDetails, scope:this, buffer:100},
					'dblclick'       : {fn:this.doCallback, scope:this},
					'loadexception'  : {fn:this.onLoadException, scope:this},
					'beforeselect'   : {fn:function(view){
				        return view.store.getRange().length > 0;
				    }},
				  'contextmenu'		 : function(view,index,node,e){e.stopEvent();var coords = e.getXY();view.select(index);file_context_menu.showAt([coords[0], coords[1]]);}
				},
				prepareData: formatData.createDelegate(this)
			});
	  
		  this.tree = new Ext.tree.TreePanel({
				autoScroll: true,
				id:'choosertree',
				animate: true,
				containerScroll: true,
				border: false,
				autoHeight:true,
				enableDD: false,
				ddGroup : 'fileMove',
				loader: new Ext.tree.TreeLoader({
					dataUrl: this.config.dirurl
				}),
				root: new Ext.tree.AsyncTreeNode({
					text: 'Files',
					draggable: false,
					id: 'source',
					expanded: true
				}),
				listeners: {
					'click': function (node,e){
											current_directory = node.attributes.url;
											if(current_directory!='') {
												Ext.getCmp('upload_button').enable();
											} else {
												Ext.getCmp('upload_button').disable();
											}
											Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{'allowedfiles':allowedfiles,dir:current_directory,method:'POST'}});
										},
					'contextmenu': function(node,e){
											node.select();
											tree_context_menu.node = node;
											tree_context_menu.show(e.getTarget());
										}
				}
			});
		  new Ext.tree.TreeSorter(this.tree, {folderSort: true});
		  
		  
		  
			var cfg = {
		    	title: 'Vyberte si súbor',
		    	id: 'img-chooser-dlg',
		    	layout: 'border',
				minWidth: 700,
				minHeight: 300,
				modal: true,
				closeAction: 'close',
				border: false,
				items:[{
					id: 'folder-panel',
					items: this.tree,tbar:[{}],
					region: 'west',
					split: true,
					width: 200,
					minWidth: 200,
					maxWidth: 450
				},{
					id: 'img-chooser-view',
					region: 'center',
					autoScroll: true,
					items: this.view,
                    tbar:[{
                    		id: 'upload_button',
                    		text: 'Nahrať na server',
                    		tooltip: 'Nahrať nový súbor na server',
                    		iconCls: 'upload_button',
                    		disabled: true,
                    		handler: function(){
                    			var upload_dialog = new Ext.ux.UploadDialog.Dialog({
															title: 'Upload Files',
															url: uploadurl,
															base_params: {action: 'upload', directory: current_directory},
															minWidth: 400,
															minHeight: 200,
															width: 400,
															height: 350,
															reset_on_hide: false,
															debug:true,
															allow_close_on_upload: false
														});
														upload_dialog.show('upload_button');
													upload_dialog.on("uploadcomplete", function() {
														Ext.getCmp('img-chooser-view').items.items[0].store.reload({params:{'allowedfiles':allowedfiles,dir:current_directory,method:'POST'}});
													});
													}
                    		},{
                    			id: 'delete_button',
                    			text: 'Vymazať súbor',
                    			tooltip: 'Vymazať vybratý súbor',
                    			iconCls: 'delete_button',
                    			disabled: true,
                    			handler: function(){do_delete_file(deleteurl);}
                    		},{
                    	text: 'Filter:'
                    },{
                    	xtype: 'textfield',
                    	id: 'filter',
                    	selectOnFocus: true,
                    	width: 100,
                    	listeners: {
                    		'render': {fn:function(){
						    	Ext.getCmp('filter').getEl().on('keyup', function(){
						    		this.filter();
						    	}, this, {buffer:500});
                    		}, scope:this}
                    	}
                    }, ' ', '-', {
                    	text: 'Zoradiť podľa:'
                    }, {
                    	id: 'sortSelect',
                    	xtype: 'combo',
				        typeAhead: true,
				        triggerAction: 'all',
				        width: 100,
				        editable: false,
				        mode: 'local',
				        displayField: 'desc',
				        valueField: 'name',
				        lazyInit: false,
				        value: 'name',
				        store: new Ext.data.SimpleStore({
					        fields: ['name', 'desc'],
					        data : [['name', 'Názvu'],['size', 'Veľkosti súboru'],['lastmod', 'Poslednej zmeny']]
					    }),
					    listeners: {
							'select': {fn:this.sortImages, scope:this}
					    }
				    }]
				},{
					id: 'img-detail-panel',
					region: 'east',
					split: true,
					width: 150,
					minWidth: 150,
					maxWidth: 250
				}],
				buttons: [{
					id: 'ok-btn',
					text: 'OK',
					handler: this.doCallback,
					scope: this
				},{
					text: 'Cancel',
					handler: function(){ this.win.close(); },
					scope: this
				}],
				keys: {
					key: 27, // Esc key
					handler: function(){ this.win.close(); },
					scope: this
				}
			};
			Ext.apply(cfg, this.config);
		    this.win = new Ext.Window(cfg);
		}
		
		this.reset();
	  this.win.show(el);
		this.callback = callback;
		this.animateTarget = el;
		return "";
	},
	
	initTemplates : function(){
		this.thumbTemplate = new Ext.XTemplate(
			'<tpl for=".">',
				'<div class="thumb-wrap" id="{name}" name="{url}">',
				'<div class="thumb"><img src="{thumb}" title="{name}"></div>',
				'<span>{shortName}</span></div>',
			'</tpl>'
		);
		this.thumbTemplate.compile();
		
		this.detailsTemplate = new Ext.XTemplate(
			'<div class="details">',
				'<tpl for=".">',
					'<img src="{thumb}" style="width:120px;"><div class="details-info">',
					'<b>Názov:</b>',
					'<span>{name}</span>',
					'<b>Veľkosť:</b>',
					'<span>{sizeString}</span>',
					'<b>Posledná zmena:</b>',
					'<span>{dateString}</span></div>',
				'</tpl>',
			'</div>'
		);
		this.detailsTemplate.compile();
	},
	
	showDetails : function(){
	    var selNode = this.view.getSelectedNodes();
	    if(Ext.getCmp('img-detail-panel')!=undefined){
			    var detailEl = Ext.getCmp('img-detail-panel').body;
				if(selNode && selNode.length > 0){
					selNode = selNode[0];
					selectedNode = selNode;
					Ext.getCmp('ok-btn').enable();
					Ext.getCmp('delete_button').enable();
				    var data = this.lookup[selNode.id];
		            detailEl.hide();
		            this.detailsTemplate.overwrite(detailEl, data);
		            detailEl.slideIn('l', {stopFx:true,duration:.2});		         
				}else{
				    Ext.getCmp('ok-btn').disable();
				    Ext.getCmp('delete_button').disable();
				    detailEl.update('');
				}
		}
	},
	
	filter : function(){
		var filter = Ext.getCmp('filter');
		this.view.store.filter('name', filter.getValue());
		this.view.select(0);
	},
	
	sortImages : function(){
		var v = Ext.getCmp('sortSelect').getValue();
    	this.view.store.sort(v, v == 'name' ? 'asc' : 'desc');
    	this.view.select(0);
    },
	
	reset : function(){
		if(this.win.rendered){
			Ext.getCmp('filter').reset();
			this.view.getEl().dom.scrollTop = 0;
		}
	    this.view.store.clearFilter();
		this.view.select(0);
	},
	
	doCallback : function(){
		
    var selNode = this.view.getSelectedRecords()[0];
		var callback = this.callback;
		var lookup = this.lookup;
		var rootel=this.rootel;
		var iswin=this.win;
		var data = selNode.data.url;
		var size=selNode.data.size;
		var sizeString=selNode.data.sizeString;		
		callback(data,rootel,iswin,size,sizeString);
		this.win.destroy();

    },
	
	onLoadException : function(v,o){
	    this.view.getEl().update('<div style="padding:10px;">Neúspešný prenos súboru</div>'); 
	}
};


String.prototype.ellipse = function(maxLength){
    if(this.length > maxLength){
        return this.substr(0, maxLength-3) + '...';
    }
    return this;
};

Ext.reg("twintrigger",Ext.form.TwinTriggerField);

// ----- TwinCombo.js

Ext.ux.TwinComboBox = Ext.extend(Ext.form.ComboBox,{
	initComponent : function(){
	//	Ext.apply(this, config);
    //Ext.apply(this.initialConfig, config);
    Ext.ux.TwinComboBox.superclass.initComponent.apply(this, arguments);
		Ext.form.TwinTriggerField.prototype.initComponent.apply(this, arguments);
	},
	getTrigger : Ext.form.TwinTriggerField.prototype.getTrigger,
	initTrigger    : Ext.form.TwinTriggerField.prototype.initTrigger,
	trigger2Class    :    'x-form-search-trigger',
	hideTrigger1 : false,

   onTrigger1Click : function(){
            this.onTriggerClick();
        }
  ,onTrigger2Click : function(){
  		//console.log(this);
			 //this.collapse();
       this.t2win = new Ext.Window({
  			width:800,
       	layout: "fit",
       	onEsc: Ext.emptyFn,
       	autoLoad: {url: this["listWindowUrl"], scripts: true}
      	});
       var twid=this;
       this.t2win.show("body");
       this.t2win.on("beforeclose",function(){
       	if(twid.mode=="local"){
       		//console.log(twid.store);
       	} else {
       		if(twid.reloaded==1) {twid.fireEvent("afteredit",twid);return true;} else {twid.store.reload({callback:function(){twid.reloaded=1;twid.t2win.close();}}); return false;}}
      	});
       return true;
   }
});

// register xtype
Ext.reg('twincombo', Ext.ux.TwinComboBox); 
 
// eof 


// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.MetaForm
 *
 * @author    Ing. Jozef Sakalos
 * @copyright (c) 2008, by Ing. Jozef Sakalos
 * @date      6. February 2007
 * @version   $Id: Ext.ux.MetaForm.js 231 2008-05-02 08:59:46Z jozo $
 *
 * @license Ext.ux.MetaForm is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

// isArray is in SVN only
if(!Ext.isArray) {
    Ext.isArray = function(v) {
        return v && 'function' === typeof v.pop;
    };
}

/**
 *
 * @class Ext.ux.MetaForm
 * @extends Ext.FormPanel
 */
Ext.ux.MetaForm = Ext.extend(Ext.FormPanel, {
    // configurables
     autoInit:true
    ,border:false
    ,frame:true
    ,loadingText:'Loading...'
    ,savingText:'Saving...'
    ,buttonMinWidth:90
    ,columnCount:1
    /**
     * createButtons {Array} array of buttons to create
     *         valid values are ['meta', 'load', defaults', 'reset', 'save', 'ok', 'cancel']
     */
    /**
     * ignoreFields: {Array} of field names to ignore when received in metaData
     */

    // {{{
    ,initComponent:function() {

        // create one item to avoid error
        Ext.apply(this, {
            items:this.items || {}
        }); // eo apply

        // get buttons if we have button creation routines
        if('function' === typeof this.getButton) {
            this.buttons = this.getButtons();
        }

        // call parent
        Ext.ux.MetaForm.superclass.initComponent.apply(this, arguments);
        
        this.addEvents('cancel', 'ok');

        // install event handlers on basic form
        this.form.on({
             beforeaction:{scope:this, fn:this.beforeAction}
            ,actioncomplete:{scope:this, fn:function(form, action) {
                // (re) configure the form if we have (new) metaData
                if('load' === action.type && action.result.metaData) {
                    this.onMetaChange(this, action.result.metaData);
                }
                // update bound data on successful submit
                else if('submit' === action.type) {
                    this.updateBoundData();
                }
            }}
        });
        this.form.trackResetOnLoad = true;

    } // eo function initComponent
    // }}}
    // {{{
    /**
     * private, changes order of execution in Ext.form.Action.Load::success
     * to allow reading of data in this server request (otherwise data would
     * be loaded to the form before onMetaChange is run from actioncomplete event
     */
    ,beforeAction:function(form, action) {
        action.success = function(response) {
            var result = this.processResponse(response);
            if(result === true || !result.success || !result.data){
                this.failureType = Ext.form.Action.LOAD_FAILURE;
                this.form.afterAction(this, false);
                return;
            }
            // original
//            this.form.clearInvalid();
//            this.form.setValues(result.data);
//            this.form.afterAction(this, true);

            this.form.afterAction(this, true);
            this.form.clearInvalid();
            this.form.setValues(result.data);
        };
    } // eo function beforeAction
    // }}}
    // {{{
    /**
     * @param {Object} data A reference to on external data object. The idea is that form can display/change an external object
     */
    ,bind:function(data) {
        this.data = data;
        this.form.setValues(this.data);
    } // eo function bind
    // }}}
    // {{{
    /**
     * override this if you want a special buttons config
     */
    ,getButtons:function() {
        var buttons = [];
        if(Ext.isArray(this.createButtons)) {
            Ext.each(this.createButtons, function(name) {
                var button;
                switch(name) {
                    case 'meta':
                        button = this.getButton(name, {
                            handler:this.load.createDelegate(this, [{params:{meta:true}}])
                        });
                    break;

                    case 'load':
                        button = this.getButton(name, {
                             scope:this
                            ,handler:this.load
                        });
                    break;

                    case 'defaults':
                        button = this.getButton(name, {
                             scope:this
                            ,handler:this.setDefaultValues
                        });
                    break;

                    case 'reset':
                        button = this.getButton(name, {
                             scope:this
                            ,handler:this.reset
                        });
                    break;

                    case 'save':
                    case 'submit':
                        button = this.getButton(name, {
                            handler:this.submit.createDelegate(this, [{params:{cmd:'setPref'}}])
                        });
                    break;

                    case 'ok':
                        button = this.getButton(name, {
                             scope:this
                            ,handler:this.onOk
                        });
                    break;

                    case 'cancel':
                        button = this.getButton(name, {
                             scope:this
                            ,handler:this.onCancel
                        });
                    break;
                }
                if(button) {
                    Ext.apply(button, {
                        minWidth:this.buttonMinWidth
                    });
                    buttons.push(button);
                }
            }, this);
        }
        return buttons;
    } // eo function getButtons
    // }}}
    // {{{
    ,getOptions:function(o) {
        var options = {
             url:this.url
            ,method:this.method || 'post'
        };
        Ext.apply(options, o);
        options.params = Ext.apply(this.baseParams || {}, o.params);
        return options;
    } // eo function getOptions
    // }}}
    // {{{
    /**
     * @return {Object} object with name/value pairs using fields.getValue() methods
     */
    ,getValues:function() {
        var values = {};
        this.form.items.each(function(f) {
            values[f.name] = f.getValue();
        });
        return values;
    } // eo function getValues
    // }}}
    // {{{
    ,load:function(o) {
        var options = this.getOptions(o);
        if(this.loadingText) {
            options.waitMsg = this.loadingText;
        }
        this.form.load(options);
    } // eo function load
    // }}}
    // {{{
    /**
     * cancel button handler - fires cancel event only
     */
    ,onCancel:function() {
        this.fireEvent('cancel', this);
    } // eo function onCancel
    // }}}
    // {{{
    /**
     * Override this if you need a custom functionality
     *
     * @param {Ext.FormPanel} this
     * @param {Object} meta Metadata
     * @return void
     */
    ,onMetaChange:function(form, meta) {
        this.removeAll();

        // declare varables
        var columns, colIndex, tabIndex, ignore = {};

        // add column layout
        this.add(new Ext.Panel({
             layout:meta.formConfig ? meta.formConfig.layout : "column"
            ,anchor:'100%'
            ,border:false
            ,defaults:(function(){
                this.columnCount = meta.formConfig ? meta.formConfig.columnCount || this.columnCount : this.columnCount;
                return Ext.apply({}, meta.formConfig || {}, {
                     columnWidth:1/this.columnCount
                    ,autoHeight:true
                    ,border:false
                    ,hideLabel:true
                    ,layout:'form'
                });
            }).createDelegate(this)()
            ,items:(function(){
                var items = [];
                for(var i = 0; i < this.columnCount; i++) {
                    items.push({
                         defaults:this.defaults
                        ,listeners:{
                            // otherwise basic form findField does not work
                            add:{scope:this, fn:this.onAdd}
                        }
                    });
                }
                return items;
            }).createDelegate(this)()
        }));
        
        columns = this.items.get(0).items;
        colIndex = 0;
        tabIndex = 1;

        if(Ext.isArray(this.ignoreFields)) {
            Ext.each(this.ignoreFields, function(f) {
                ignore[f] = true;
            });
        }
        // loop through metadata colums or fields
        // format follows grid column model structure
        Ext.each(meta.columns || meta.fields, function(item) {
            if(true === ignore[item.name]) {
                return;
            }
            var config = Ext.apply({}, item.editor, {
                 name:item.name || item.dataIndex
                ,fieldLabel:item.fieldLabel || item.header
                ,defaultValue:item.defaultValue
                ,xtype:item.editor && item.editor.xtype ? item.editor.xtype : 'textfield'
            });

            // handle regexps
            if(config.editor && config.editor.regex) {
                config.editor.regex = new RegExp(item.editor.regex);
            }

            // to avoid checkbox misalignment
            if('checkbox' === config.xtype) {
                Ext.apply(config, {
                      boxLabel:' '
                     ,checked:item.defaultValue
                });
            }
            if(meta.formConfig.msgTarget) {
                config.msgTarget = meta.formConfig.msgTarget;
            }

            // add to columns on ltr principle
            config.tabIndex = tabIndex++;
            columns.get(colIndex++).add(config);
            colIndex = colIndex === this.columnCount ? 0 : colIndex;

        }, this);
        this.doLayout();
    } // eo function onMetaChange
    // }}}
    // {{{
    ,onOk:function() {
        this.updateBoundData();
        this.fireEvent('ok', this);
    }
    // }}}
    // {{{
    ,onRender:function() {
        // call parent
        Ext.ux.MetaForm.superclass.onRender.apply(this, arguments);

        this.form.waitMsgTarget = this.el;

        if(true === this.autoInit) {
            this.load({params:{meta:true}});
        }
        else if ('object' === typeof this.autoInit) {
            this.load(this.autoInit);
        }
    } // eo function onRender
    // }}}
    // {{{
    /**
     * private, removes all items from both formpanel and basic form
     */
    ,removeAll:function() {
        // remove border from header
        var hd = this.body.up('div.x-panel-bwrap').prev();
        if(hd) {
            hd.applyStyles({border:'none'});
        }
        // remove form panel items
        this.items.each(this.remove, this);

        // remove basic form items
        this.form.items.clear();
    } // eo function removeAllItems
    // }}}
    // {{{
    ,reset:function() {
        this.form.reset();
    } // eo function reset
    // }}}
    // {{{
    ,setDefaultValues:function() {
        this.form.items.each(function(item) {
            item.setValue(item.defaultValue);
        });
    } // eo function setDefaultValues
    // }}}
    // {{{
    ,submit:function(o) {
        var options = this.getOptions(o);
        if(this.savingText) {
            options.waitMsg = this.savingText;
        }
        this.form.submit(options);
    } // eo function submit
    // }}}
    // {{{
    ,updateBoundData:function() {
        if(this.data) {
            Ext.apply(this.data, this.getValues());
        }
    } // eo function updateBoundData
    // }}}

});

// register xtype
Ext.reg('metaform', Ext.ux.MetaForm);

// eof 

Ext.override(Ext.form.Field, {
	markInvalid : function(msg){
		if(!this.rendered || this.preventMark){
			return;
		}
		var markEl = this.markEl || this.el;
		markEl.addClass(this.invalidClass);
		msg = msg || this.invalidText;
		switch(this.msgTarget){
			case 'qtip':
				markEl.dom.qtip = msg;
				markEl.dom.qclass = 'x-form-invalid-tip';
				if(Ext.QuickTips){
					Ext.QuickTips.enable();
				}
				break;
			case 'title':
				markEl.dom.title = msg;
				break;
			case 'under':
				if(!this.errorEl){
					var elp = this.getErrorCt();
					if(!elp){
						markEl.dom.title = msg;
						break;
					}
					this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
					this.errorEl.setWidth(elp.getWidth(true)-20);
				}
				this.errorEl.update(msg);
				Ext.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
				break;
			case 'side':
				if(!this.errorIcon){
					var elp = this.getErrorCt();
					if(!elp){
						markEl.dom.title = msg;
						break;
					}
					this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
				}
				this.alignErrorIcon();
				this.errorIcon.dom.qtip = msg;
				this.errorIcon.dom.qclass = 'x-form-invalid-tip';
				this.errorIcon.show();
				this.on('resize', this.alignErrorIcon, this);
				break;
			default:
				var t = Ext.getDom(this.msgTarget);
				t.innerHTML = msg;
				t.style.display = this.msgDisplay;
				break;
		}
		this.fireEvent('invalid', this, msg);
	},
	clearInvalid : function(){
		if(!this.rendered || this.preventMark){
			return;
		}
		var markEl = this.markEl || this.el;
		markEl.removeClass(this.invalidClass);
		switch(this.msgTarget){
			case 'qtip':
				markEl.dom.qtip = '';
				break;
			case 'title':
				markEl.dom.title = '';
				break;
			case 'under':
				if(this.errorEl){
					Ext.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
				}else{
					markEl.dom.title = '';
				}
				break;
			case 'side':
				if(this.errorIcon){
					this.errorIcon.dom.qtip = '';
					this.errorIcon.hide();
					this.un('resize', this.alignErrorIcon, this);
				}else{
					markEl.dom.title = '';
				}
				break;
			default:
				var t = Ext.getDom(this.msgTarget);
				t.innerHTML = '';
				t.style.display = 'none';
				break;
		}
		this.fireEvent('valid', this);
	},
	alignErrorIcon : function(){
		this.errorIcon.alignTo(this.markEl || this.el, 'tl-tr', [2, 0]);
	}
});
Ext.override(Ext.form.Checkbox, {
	onRender: function(ct, position){
		Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
		if(this.inputValue !== undefined){
			this.el.dom.value = this.inputValue;
		}
		this.el.removeClass(this.baseCls);
		//this.el.addClass('x-hidden');
		this.innerWrap = this.el.wrap({
			//tabIndex: this.tabIndex,
			cls: this.baseCls+'-wrap-inner'
		});
		this.wrap = this.innerWrap.wrap({cls: this.baseCls+'-wrap'});
		this.imageEl = this.innerWrap.createChild({
			tag: 'img',
			src: Ext.BLANK_IMAGE_URL,
			cls: this.baseCls
		});
		if(this.boxLabel){
			this.labelEl = this.innerWrap.createChild({
				tag: 'label',
				htmlFor: this.el.id,
				cls: 'x-form-cb-label',
				html: this.boxLabel
			});
		}
		//this.imageEl = this.innerWrap.createChild({
			//tag: 'img',
			//src: Ext.BLANK_IMAGE_URL,
			//cls: this.baseCls
		//}, this.el);
		if(this.checked){
			this.setValue(true);
		}else{
			this.checked = this.el.dom.checked;
		}
		this.originalValue = this.checked;
		this.markEl = this.innerWrap;
	},
	afterRender: function(){
		Ext.form.Checkbox.superclass.afterRender.call(this);
		//this.wrap[this.checked ? 'addClass' : 'removeClass'](this.checkedCls);
		this.imageEl[this.checked ? 'addClass' : 'removeClass'](this.checkedCls);
	},
	initCheckEvents: function(){
		//this.innerWrap.removeAllListeners();
		this.innerWrap.addClassOnOver(this.overCls);
		this.innerWrap.addClassOnClick(this.mouseDownCls);
		this.innerWrap.on('click', this.onClick, this);
		//this.innerWrap.on('keyup', this.onKeyUp, this);
		if(this.validationEvent !== false){
			this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
		}
	},
	onFocus: function(e) {
		Ext.form.Checkbox.superclass.onFocus.call(this, e);
		//this.el.addClass(this.focusCls);
		this.innerWrap.addClass(this.focusCls);
	},
	onBlur: function(e) {
		Ext.form.Checkbox.superclass.onBlur.call(this, e);
		//this.el.removeClass(this.focusCls);
		this.innerWrap.removeClass(this.focusCls);
	},
	onClick: function(e){
		if (e.getTarget().htmlFor != this.el.dom.id) {
			if (e.getTarget() != this.el.dom) {
				this.el.focus();
			}
			if (!this.disabled && !this.readOnly) {
				this.toggleValue();
			}
		}
		//e.stopEvent();
	},
	onEnable: Ext.form.Checkbox.superclass.onEnable,
	onDisable: Ext.form.Checkbox.superclass.onDisable,
	onKeyUp: undefined,
	setValue: function(v) {
		var checked = this.checked;
		this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
		if(this.rendered){
			this.el.dom.checked = this.checked;
			this.el.dom.defaultChecked = this.checked;
			//this.wrap[this.checked ? 'addClass' : 'removeClass'](this.checkedCls);
			this.imageEl[this.checked ? 'addClass' : 'removeClass'](this.checkedCls);
		}
		if(checked != this.checked){
			this.fireEvent("check", this, this.checked);
			if(this.handler){
				this.handler.call(this.scope || this, this, this.checked);
			}
		}
	},
	getResizeEl: function() {
		//if(!this.resizeEl){
			//this.resizeEl = Ext.isSafari ? this.wrap : (this.wrap.up('.x-form-element', 5) || this.wrap);
		//}
		//return this.resizeEl;
		return this.wrap;
	},
	markInvalid: Ext.form.Checkbox.superclass.markInvalid,
	clearInvalid: Ext.form.Checkbox.superclass.clearInvalid,
	validationEvent: 'click',
	validateOnBlur: false,
	validateValue: function(value){
		if(this.vtype){
			var vt = Ext.form.VTypes;
			if(!vt[this.vtype](value, this)){
				this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
				return false;
			}
		}
		if(typeof this.validator == "function"){
			var msg = this.validator(value);
			if(msg !== true){
				this.markInvalid(msg);
				return false;
			}
		}
		return true;
	}
});
Ext.override(Ext.form.Radio, {
	checkedCls: 'x-form-radio-checked',
	markInvalid: Ext.form.Radio.superclass.markInvalid,
	clearInvalid: Ext.form.Radio.superclass.clearInvalid
});


// Ext.ux.grid.GridSummary.js
// summary plugin for Grid

Ext.ns('Ext.ux.grid');

Ext.ux.grid.GridSummary = function(config) {
        Ext.apply(this, config);
};

Ext.extend(Ext.ux.grid.GridSummary, Ext.util.Observable, {
    init : function(grid) {
        this.grid = grid;
        this.cm = grid.getColumnModel();
        this.view = grid.getView();

        var v = this.view;

        // override GridView's onLayout() method
        v.onLayout = this.onLayout;

        v.afterMethod('render', this.refreshSummary, this);
        v.afterMethod('refresh', this.refreshSummary, this);
        v.afterMethod('syncScroll', this.syncSummaryScroll, this);
        v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
        v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
        v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);

        // update summary row on store's add/remove/clear/update events
        grid.store.on({
            add: this.refreshSummary,
            remove: this.refreshSummary,
            clear: this.refreshSummary,
            update: this.refreshSummary,
            scope: this
        });

        if (!this.rowTpl) {
            this.rowTpl = new Ext.Template(
                '<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">',
                    '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                        '<tbody><tr>{cells}</tr></tbody>',
                    '</table>',
                '</div>'
            );
            this.rowTpl.disableFormats = true;
        }
        this.rowTpl.compile();

        if (!this.cellTpl) {
            this.cellTpl = new Ext.Template(
                '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
                    '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
                "</td>"
            );
            this.cellTpl.disableFormats = true;
        }
        this.cellTpl.compile();
    },

    calculate : function(rs, cm) {
        var data = {}, cfg = cm.config;
        for (var i = 0, len = cfg.length; i < len; i++) { // loop through all columns in ColumnModel
            var cf = cfg[i], // get column's configuration
                cname = cf.dataIndex; // get column dataIndex

            // initialise grid summary row data for
            // the current column being worked on
            data[cname] = 0;

            if (cf.summaryType) {
                for (var j = 0, jlen = rs.length; j < jlen; j++) {
                    var r = rs[j]; // get a single Record
                    data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j);
                }
            }
        }

        return data;
    },

    onLayout : function(vw, vh) {
        if (Ext.type(vh) != 'number') { // handles grid's height:'auto' config
            return;
        }
        // note: this method is scoped to the GridView
        if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) {
            // readjust gridview's height only if grid summary row is visible
            this.scroller.setHeight(vh - this.summary.getHeight());
        }
    },

    syncSummaryScroll : function() {
        var mb = this.view.scroller.dom;

        this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft;
        this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
    },

    doWidth : function(col, w, tw) {
        var s = this.view.summary.dom;

        s.firstChild.style.width = tw;
        s.firstChild.rows[0].childNodes[col].style.width = w;
    },

    doAllWidths : function(ws, tw) {
        var s = this.view.summary.dom, wlen = ws.length;

        s.firstChild.style.width = tw;

        var cells = s.firstChild.rows[0].childNodes;

        for (var j = 0; j < wlen; j++) {
            cells[j].style.width = ws[j];
        }
    },

    doHidden : function(col, hidden, tw) {
        var s = this.view.summary.dom,
            display = hidden ? 'none' : '';

        s.firstChild.style.width = tw;
        s.firstChild.rows[0].childNodes[col].style.display = display;
    },

    renderSummary : function(o, cs, cm) {
        cs = cs || this.view.getColumnData();
        var cfg = cm.config,
            buf = [],
            last = cs.length - 1;

        for (var i = 0, len = cs.length; i < len; i++) {
            var c = cs[i], cf = cfg[i], p = {};

            p.id = c.id;
            p.style = c.style;
            p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');

            if (cf.summaryType || cf.summaryRenderer) {
                p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
            } else {
                p.value = '';
            }
            if (p.value == undefined || p.value === "") p.value = "&#160;";
            buf[buf.length] = this.cellTpl.apply(p);
        }

        return this.rowTpl.apply({
            tstyle: 'width:' + this.view.getTotalWidth() + ';',
            cells: buf.join('')
        });
    },

    refreshSummary : function() {
        var g = this.grid, ds = g.store,
            cs = this.view.getColumnData(),
            cm = this.cm,
            rs = ds.getRange(),
            data = this.calculate(rs, cm),
            buf = this.renderSummary({data: data}, cs, cm);

        if (!this.view.summaryWrap) {
            this.view.summaryWrap = Ext.DomHelper.insertAfter(this.view.scroller, {
                tag: 'div',
                cls: 'x-grid3-gridsummary-row-inner'
            }, true);
        }
        this.view.summary = this.view.summaryWrap.update(buf).first();
    },

    toggleSummary : function(visible) { // true to display summary row
        var el = this.grid.getGridEl();

        if (el) {
            if (visible === undefined) {
                visible = el.hasClass('x-grid-hide-gridsummary');
            }
            el[visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary');

            this.view.layout(); // readjust gridview height
        }
    },

    getSummaryNode : function() {
        return this.view.summary
    }
});
Ext.reg('gridsummary', Ext.ux.grid.GridSummary);

/*
 * all Calculation methods are called on each Record in the Store
 * with the following 5 parameters:
 *
 * v - cell value
 * record - reference to the current Record
 * colName - column name (i.e. the ColumnModel's dataIndex)
 * data - the cumulative data for the current column + summaryType up to the current Record
 * rowIdx - current row index
 */
Ext.ux.grid.GridSummary.Calculations = {
    sum : function(v, record, colName, data, rowIdx) {
        return data[colName] + Ext.num(v, 0);
    },

    count : function(v, record, colName, data, rowIdx) {
        return rowIdx + 1;
    },

    max : function(v, record, colName, data, rowIdx) {
        return Math.max(Ext.num(v, 0), data[colName]);
    },

    min : function(v, record, colName, data, rowIdx) {
        return Math.min(Ext.num(v, 0), data[colName]);
    },

    average : function(v, record, colName, data, rowIdx) {
        var t = data[colName] + Ext.num(v, 0), count = record.store.getCount();
        return rowIdx == count - 1 ? (t / count) : t;
    }
}

// GridGroupSummary.js
// sluzi na sumarizaciu jendotlivych skupin v gride
/*
 * Ext JS Library 2.2.1
 * Copyright(c) 2006-2009, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

Ext.grid.GroupSummary = function(config){
    Ext.apply(this, config);
};

Ext.extend(Ext.grid.GroupSummary, Ext.util.Observable, {
    init : function(grid){
        this.grid = grid;
        this.cm = grid.getColumnModel();
        this.view = grid.getView();

        var v = this.view;
        v.doGroupEnd = this.doGroupEnd.createDelegate(this);

        v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
        v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
        v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
        v.afterMethod('onUpdate', this.doUpdate, this);
        v.afterMethod('onRemove', this.doRemove, this);

        if(!this.rowTpl){
            this.rowTpl = new Ext.Template(
                '<div class="x-grid3-summary-row" style="{tstyle}">',
                '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                    '<tbody><tr>{cells}</tr></tbody>',
                '</table></div>'
            );
            this.rowTpl.disableFormats = true;
        }
        this.rowTpl.compile();

        if(!this.cellTpl){
            this.cellTpl = new Ext.Template(
                '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
                '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
                "</td>"
            );
            this.cellTpl.disableFormats = true;
        }
        this.cellTpl.compile();
    },

    toggleSummaries : function(visible){
        var el = this.grid.getGridEl();
        if(el){
            if(visible === undefined){
                visible = el.hasClass('x-grid-hide-summary');
            }
            el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');
        }
    },

    renderSummary : function(o, cs){
        cs = cs || this.view.getColumnData();
        var cfg = this.cm.config;

        var buf = [], c, p = {}, cf, last = cs.length-1;
        for(var i = 0, len = cs.length; i < len; i++){
            c = cs[i];
            cf = cfg[i];
            p.id = c.id;
            p.style = c.style;
            p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
            if(cf.summaryType || cf.summaryRenderer){
                p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
            }else{
                p.value = '';
            }
            if(p.value == undefined || p.value === "") p.value = "&#160;";
            buf[buf.length] = this.cellTpl.apply(p);
        }

        return this.rowTpl.apply({
            tstyle: 'width:'+this.view.getTotalWidth()+';',
            cells: buf.join('')
        });
    },

    calculate : function(rs, cs){
        var data = {}, r, c, cfg = this.cm.config, cf;
        for(var j = 0, jlen = rs.length; j < jlen; j++){
            r = rs[j];
            for(var i = 0, len = cs.length; i < len; i++){
                c = cs[i];
                cf = cfg[i];
                if(cf.summaryType){
                    data[c.name] = Ext.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
                }
            }
        }
        return data;
    },

    doGroupEnd : function(buf, g, cs, ds, colCount){
        var data = this.calculate(g.rs, cs);
        buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');
    },

    doWidth : function(col, w, tw){
        var gs = this.view.getGroups(), s;
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            s.firstChild.rows[0].childNodes[col].style.width = w;
        }
    },

    doAllWidths : function(ws, tw){
        var gs = this.view.getGroups(), s, cells, wlen = ws.length;
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            cells = s.firstChild.rows[0].childNodes;
            for(var j = 0; j < wlen; j++){
                cells[j].style.width = ws[j];
            }
        }
    },

    doHidden : function(col, hidden, tw){
        var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            s.firstChild.rows[0].childNodes[col].style.display = display;
        }
    },

    // Note: requires that all (or the first) record in the 
    // group share the same group value. Returns false if the group
    // could not be found.
    refreshSummary : function(groupValue){
        return this.refreshSummaryById(this.view.getGroupId(groupValue));
    },

    getSummaryNode : function(gid){
        var g = Ext.fly(gid, '_gsummary');
        if(g){
            return g.down('.x-grid3-summary-row', true);
        }
        return null;
    },

    refreshSummaryById : function(gid){
        var g = document.getElementById(gid);
        if(!g){
            return false;
        }
        var rs = [];
        this.grid.store.each(function(r){
            if(r._groupId == gid){
                rs[rs.length] = r;
            }
        });
        var cs = this.view.getColumnData();
        var data = this.calculate(rs, cs);
        var markup = this.renderSummary({data: data}, cs);

        var existing = this.getSummaryNode(gid);
        if(existing){
            g.removeChild(existing);
        }
        Ext.DomHelper.append(g, markup);
        return true;
    },

    doUpdate : function(ds, record){
        this.refreshSummaryById(record._groupId);
    },

    doRemove : function(ds, record, index, isUpdate){
        if(!isUpdate){
            this.refreshSummaryById(record._groupId);
        }
    },

    showSummaryMsg : function(groupValue, msg){
        var gid = this.view.getGroupId(groupValue);
        var node = this.getSummaryNode(gid);
        if(node){
            node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
        }
    }
});

Ext.grid.GroupSummary.Calculations = {
    'sum' : function(v, record, field){
        return v + (record.data[field]||0);
    },

    'count' : function(v, record, field, data){
        return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
    },

    'max' : function(v, record, field, data){
        var v = record.data[field];
        var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];
        return v > max ? (data[field+'max'] = v) : max;
    },

    'min' : function(v, record, field, data){
        var v = record.data[field];
        var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];
        return v < min ? (data[field+'min'] = v) : min;
    },

    'average' : function(v, record, field, data){
        var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
        var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));
        return t === 0 ? 0 : t / c;
    }
}

Ext.grid.HybridSummary = Ext.extend(Ext.grid.GroupSummary, {
    calculate : function(rs, cs){
        var gcol = this.view.getGroupField();
        var gvalue = rs[0].data[gcol];
        var gdata = this.getSummaryData(gvalue);
        return gdata || Ext.grid.HybridSummary.superclass.calculate.call(this, rs, cs);
    },

    updateSummaryData : function(groupValue, data, skipRefresh){
        var json = this.grid.store.reader.jsonData;
        if(!json.summaryData){
            json.summaryData = {};
        }
        json.summaryData[groupValue] = data;
        if(!skipRefresh){
            this.refreshSummary(groupValue);
        }
    },

    getSummaryData : function(groupValue){
        var json = this.grid.store.reader.jsonData;
        if(json && json.summaryData){
            return json.summaryData[groupValue];
        }
        return null;
    }
});

Ext.menu.ComboItem = function(config){
    Ext.menu.ComboItem.superclass.constructor.call(this, new Ext.form.ComboBox(config), config);
    /** The Ext.DatePicker object @type Ext.DatePicker */
    this.combo = this.component;
   // this.addEvents('select');
   	this.combo.on("render", function(combo){
        combo.getEl().swallowEvent("click");
        combo.getEl().swallowEvent("activate");
        combo.getEl().swallowEvent("select");
    });

    this.combo.on("select", this.onSelect, this);
};

Ext.extend(Ext.menu.ComboItem, Ext.menu.Adapter, {
    // private
    lazyRender:true,
    hideOnClick: false,
    hideParent:false,
    canActivate: true,
    onSelect : function(combo, date){
        Ext.menu.ComboItem.superclass.handleClick.call(this);
    }
});

Ext.menu.ComboMenu = function(config){
    Ext.menu.ComboMenu.superclass.constructor.call(this, config);
    this.plain = true;
    var di = new Ext.menu.ComboItem(config);
    this.add(di);
    this.combo = di.combo;

    //this.relayEvents(di, ["select"]);
};
Ext.extend(Ext.menu.ComboMenu, Ext.menu.Menu, {
		lazyRender: true,
		
    // private
    beforeDestroy : function() {
        this.combo.destroy();
    }
});