text: /**
   * Filter by a configurable Ext.picker.DatePicker menu
   *
   * This filter allows for the following configurations:
   *
   * - Any of the normal configs will be passed through to either component.
   * - There can be a docked config.
   * - The timepicker can be on the right or left (datepicker, too, of course).
   * - Choose which component will initiate the filtering, i.e., the event can be
   *   configured to be bound to either the datepicker or the timepicker, or if
   *   there is a docked config it be automatically have the handler bound to it.
   *
   * Although not shown here, this class accepts all configuration options
   * for {@link Ext.picker.Date} and {@link Ext.picker.Time}.
   *
   * In the case that a custom dockedItems config is passed in, the
   * class will handle binding the default listener to it so the
   * developer need not worry about having to do it.
   *
   * The default dockedItems position and the toolbar's
   * button text can be passed a config for convenience, i.e.,:
   *
   *     dock: {
   *        buttonText: 'Click to Filter',
   *        dock: 'left'
   *     }
   *
   * Or, pass in a full dockedItems config:
   *
   *     dock: {
   *        dockedItems: {
   *            xtype: 'toolbar',
   *            dock: 'bottom',
   *            ...
   *        }
   *     }
   *
   * Or, give a value of `true` to accept dock defaults:
   *
   *     dock: true
   *
   * But, it must be one or the other.
   *
   * Example Usage:
   *
   *     var filters = Ext.create('Ext.ux.grid.GridFilters', {
   *         ...
   *         filters: [{
   *             // required configs
   *             type: 'datetime',
   *             dataIndex: 'date',
   *
   *             // optional configs
   *             positionDatepickerFirst: false,
   *             //selectDateToFilter: false, // this is overridden b/c of the presence of the dock cfg object
   *
   *             date: {
   *                 format: 'm/d/Y',
   *             },
   *
   *             time: {
   *                 format: 'H:i:s A',
   *                 increment: 1
   *             },
   *
   *             dock: {
   *                 buttonText: 'Click to Filter',
   *                 dock: 'left'
   *
   *                 // allows for custom dockedItems cfg
   *                 //dockedItems: {}
   *             }
   *         }]
   *     });
   *
   * In the above example, note that the filter is being passed a {@link #date} config object,
   * a {@link #time} config object and a {@link #dock} config. These are all optional.
   *
   * As for positioning, the datepicker will be on the right, the timepicker on the left
   * and the docked items will be docked on the left. In addition, since there's a {@link #dock}
   * config, clicking the button in the dock will trigger the filtering.
   */
  Ext.define('Ext.ux.grid.filter.DateTimeFilter', {
      extend: 'Ext.ux.grid.filter.DateFilter',
      alias: 'gridfilter.datetime',
  
      /**
       * @private
       */
      dateDefaults: {
          xtype: 'datepicker',
          format: 'm/d/Y'
      },
  
      /**
       * @private
       */
      timeDefaults: {
          xtype: 'timepicker',
          width: 100,
          height: 200,
          format: 'g:i A'
      },
  
      /**
       * @private
       */
      dockDefaults: {
          dock: 'top',
          buttonText: 'Filter'
      },
  
      /**
       * @cfg {Object} date
       * A {@link Ext.picker.Date} can be configured here.
       * Uses {@link #dateDefaults} by default.
       */
  
      /**
       * @cfg {Object} time
       * A {@link Ext.picker.Time} can be configured here.
       * Uses {@link #timeDefaults} by default.
       */
  
      /**
       * @cfg {Boolean/Object} dock
       * A {@link Ext.panel.AbstractPanel#cfg-dockedItems} can be configured here.
       * A `true` value will use the {@link #dockDefaults} default configuration.
       * If present, the button in the docked items will initiate the filtering.
       */
  
      /**
       * @cfg {Boolean} [selectDateToFilter=true]
       * By default, the datepicker has the default event listener bound to it.
       * Setting to `false` will bind it to the timepicker.
       *
       * The config will be ignored if there is a `dock` config.
       */
      selectDateToFilter: true,
  
      /**
       * @cfg {Boolean} [positionDatepickerFirst=true]
       * Positions the datepicker within its container.
       * A `true` value will place it on the left in the container.
       * Set to `false` if the timepicker should be placed on the left.
       * Defaults to `true`.
       */
      positionDatepickerFirst: true,
  
      reTime: /\s(am|pm)/i,
      reItemId: /\w*-(\w*)$/,
  
      /**
       * Replaces the selected value of the timepicker with the default 00:00:00.
       * @private
       * @param {Object} date
       * @param {Ext.picker.Time} timepicker
       * @return Date object
       */
      addTimeSelection: function (date, timepicker) {
          var me = this,
              selection = timepicker.getSelectionModel().getSelection(),
              time, len, fn, val,
              i = 0,
              arr = [],
              timeFns = ['setHours', 'setMinutes', 'setSeconds', 'setMilliseconds'];
  
  
          if (selection.length) {
              time = selection[0].get('disp');
  
              // Loop through all of the splits and add the time values.
              arr = time.replace(me.reTime, '').split(':');
  
              for (len = arr.length; i < len; i++) {
                  fn = timeFns[i];
                  val = arr[i];
  
                  if (val) {
                      date[fn](parseInt(val, 10));
                  }
              }
          }
  
          return date;
      },
  
      /**
       * @private
       * Template method that is to initialize the filter and install required menu items.
       */
      init: function (config) {
          var me = this,
              dateCfg = Ext.applyIf(me.date || {}, me.dateDefaults),
              timeCfg = Ext.applyIf(me.time || {}, me.timeDefaults),
              dockCfg = me.dock, // should not default to empty object
              defaultListeners = {
                  click: {
                      scope: me,
                      click: me.onMenuSelect
                  },
                  select: {
                      scope: me,
                      select: me.onMenuSelect
                  }
              },
              pickerCtnCfg, i, len, item, cfg,
              items = [dateCfg, timeCfg],
  
              // we need to know the datepicker's position in the items array
              // for when the itemId name is bound to it before adding to the menu
              datepickerPosition = 0;
  
          if (!me.positionDatepickerFirst) {
              items = items.reverse();
              datepickerPosition = 1;
          }
  
          pickerCtnCfg = Ext.apply(me.pickerOpts, {
              xtype: !dockCfg ? 'container' : 'panel',
              layout: 'hbox',
              items: items
          });
  
          // If there's no dock config then bind the default listener to the desired picker.
          if (!dockCfg) {
              if (me.selectDateToFilter) {
                  dateCfg.listeners = defaultListeners.select;
              } else {
                  timeCfg.listeners = defaultListeners.select;
              }
          } else if (dockCfg) {
              me.selectDateToFilter = null;
  
              if (dockCfg.dockedItems) {
                  pickerCtnCfg.dockedItems = dockCfg.dockedItems;
                  // TODO: allow config that will tell which item to bind the listener to
                  // right now, it's using the first item
                  pickerCtnCfg.dockedItems.items[dockCfg.bindToItem || 0].listeners = defaultListeners.click;
              } else {
                  // dockCfg can be `true` if button text and dock position defaults are wanted
                  if (Ext.isBoolean(dockCfg)) {
                      dockCfg = {};
                  }
                  dockCfg = Ext.applyIf(dockCfg, me.dockDefaults);
                  pickerCtnCfg.dockedItems = {
                      xtype: 'toolbar',
                      dock: dockCfg.dock,
                      items: [
                          {
                              xtype: 'button',
                              text: dockCfg.buttonText,
                              flex: 1,
                              listeners: defaultListeners.click
                          }
                      ]   
                  };
              }
          }
  
          me.fields = {};
          for (i = 0, len = me.menuItems.length; i < len; i++) {
              item = me.menuItems[i];
              if (item !== '-') {
                  pickerCtnCfg.items[datepickerPosition].itemId = item;
  
                  cfg = {
                      itemId: 'range-' + item,
                      text: me[item + 'Text'],
                      menu: Ext.create('Ext.menu.Menu', {
                          items: pickerCtnCfg
                      }),
                      listeners: {
                          scope: me,
                          checkchange: me.onCheckChange
                      }
                  };
                  item = me.fields[item] = Ext.create('Ext.menu.CheckItem', cfg);
              }
              me.menu.add(item);
          }
          me.values = {};
      },
  
      /**
       * @private
       */
      onCheckChange: function (item, checked) {
          var me = this,
              menu = item.menu,
              timepicker = menu.down('timepicker'),
              datepicker = menu.down('datepicker'),
              itemId = datepicker.itemId,
              values = me.values;
  
          if (checked) {
              values[itemId] = me.addTimeSelection(datepicker.value, timepicker);
          } else {
              delete values[itemId];
          }
          me.setActive(me.isActivatable());
          me.fireEvent('update', me);
      },
  
      /** 
       * Handler for when the DatePicker for a field fires the 'select' event
       * @param {Ext.picker.Date} picker
       * @param {Object} date
       */
      onMenuSelect: function (picker, date) {
          // NOTE: we need to redefine the picker.
          var me = this,
              menu = me.menu,
              checkItemId = menu.getFocusEl().itemId.replace(me.reItemId, '$1'),
              fields = me.fields,
              field;
  
          picker = menu.queryById(checkItemId);
          field = me.fields[picker.itemId];
          field.setChecked(true);
  
          if (field == fields.on) {
              fields.before.setChecked(false, true);
              fields.after.setChecked(false, true);
          } else {
              fields.on.setChecked(false, true);
              if (field == fields.after && me.getFieldValue('before') < date) {
                  fields.before.setChecked(false, true);
              } else if (field == fields.before && me.getFieldValue('after') > date) {
                  fields.after.setChecked(false, true);
              }   
          }   
          me.fireEvent('update', me);
  
          // The timepicker's getBubbleTarget() returns the boundlist's implementation,
          // so it doesn't look up ownerCt chain (it looks up this.pickerField).
          // This is a problem :)
          // This can be fixed by just walking up the ownerCt chain
          // (same thing, but confusing without comment).
          picker.ownerCt.ownerCt.hide();
      },
  
      /**
       * @private
       * Template method that is to get and return serialized filter data for
       * transmission to the server.
       * @return {Object/Array} An object or collection of objects containing
       * key value pairs representing the current configuration of the filter.
       */
      getSerialArgs: function () {
          var me = this,
              key,
              fields = me.fields,
              args = [];
  
          for (key in fields) {
              if (fields[key].checked) {
                  args.push({
                      type: 'datetime',
                      comparison: me.compareMap[key],
                      value: Ext.Date.format(me.getFieldValue(key), (me.date.format || me.dateDefaults.format) + ' ' + (me.time.format || me.timeDefaults.format))
                  });
              }
          }
          return args;
      },
  
      /**
       * @private
       * Template method that is to set the value of the filter.
       * @param {Object} value The value to set the filter
       * @param {Boolean} preserve true to preserve the checked status
       * of the other fields.  Defaults to false, unchecking the
       * other fields
       */
      setValue: function (value, preserve) {
          var me = this,
              fields = me.fields,
              key,
              val,
              datepicker;
  
          for (key in fields) {
              val = value[key];
              if (val) {
                  datepicker = me.menu.down('datepicker[itemId="' + key + '"]');
                  // Note that calling the Ext.picker.Date:setValue() calls Ext.Date.clearTime(),
                  // which we don't want, so just call update() instead and set the value on the component.
                  datepicker.update(val);
                  datepicker.value = val;
  
                  fields[key].setChecked(true);
              } else if (!preserve) {
                  fields[key].setChecked(false);
              }
          }
          me.fireEvent('update', me);
      },
  
      /**
       * Template method that is to validate the provided Ext.data.Record
       * against the filters configuration.
       * @param {Ext.data.Record} record The record to validate
       * @return {Boolean} true if the record is valid within the bounds
       * of the filter, false otherwise.
       */
      validateRecord: function (record) {
          // remove calls to Ext.Date.clearTime
          var me = this,
              key,
              pickerValue,
              val = record.get(me.dataIndex);
  
          if(!Ext.isDate(val)){
              return false;
          }
  
          val = val.getTime();
  
          for (key in me.fields) {
              if (me.fields[key].checked) {
                  pickerValue = me.getFieldValue(key).getTime();
                  if (key == 'before' && pickerValue <= val) {
                      return false;
                  }
                  if (key == 'after' && pickerValue >= val) {
                      return false;
                  }
                  if (key == 'on' && pickerValue != val) {
                      return false;
                  }
              }
          }
          return true;
      }
  });
  