Программный интерфейс

Показывать:
/**
 * ### Визуальный компонент - табличное поле объекта
 *
 * © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
 *
 * @module  widgets
 * @submodule wdg_otabular
 * @requires common
 */


/**
 * ### Визуальный компонент - табличное поле объекта
 * - Предназначен для отображения и редактирования {{#crossLink "TabularSection"}}табличной части{{/crossLink}}
 * - Унаследован от [dhtmlXGridObject](http://docs.dhtmlx.com/grid__index.html)
 * - Состав и типы колонок формируются автоматически по описанию метаданны
 * - Программное изменение состава строк и значений в полях строк синхронно отображается в элементе управления
 * - Редактирование в элементе управления синхронно изменяет свойства табличной части связанного объекта
 *
 * Особенность dhtmlx: экземпляр создаётся не конструктором, а функцией `attachTabular` (без `new`) и размещается в ячейке dhtmlXCellObject
 *
 * @class OTabular
 * @param attr
 * @param attr.parent {HTMLElement} - контейнер, в котором будет размещен элемент
 * @param attr.obj {DataObj} - ссылка на редактируемый объект
 * @param attr.ts {String} - имя табличной части
 * @param [attr.metadata] {Object} - описание метаданных табличной части. Если не указано, описание запрашивается у объекта
 * @param [attr.selection] {Object} - в ключах имена полей, в значениях значения фильтра или объект {like: "значение"} или {not: значение}
 * @constructor
 * @menuorder 53
 * @tooltip Редактор таличной части
 */
dhtmlXCellObject.prototype.attachTabular = function(attr) {


  var _obj = attr.obj,
    _tsname = attr.ts,
    _ts = _obj[_tsname],
    _mgr = _obj._manager,
    _meta = attr.metadata || _mgr.metadata().tabular_sections[_tsname].fields,
    _cell = this,
    _source = {},
    _selection = attr.selection;
  if(!_md.ts_captions(_mgr.class_name, _tsname, _source))
    return;

  var _grid = this.attachGrid(),
    _toolbar = this.attachToolbar(),
    _destructor = _grid.destructor,
    _pwnd = {
      // обработчик выбора ссылочных значений из внешних форм, открываемых полями со списками
      on_select: function (selv) {
        tabular_on_edit(2, null, null, selv);
      },
      pwnd: attr.pwnd || _cell,
      is_tabular: true
    };
  _grid.setDateFormat("%d.%m.%Y %H:%i");
  _grid.enableAccessKeyMap();

  /**
   * добавляет строку табчасти
   */
  _grid._add_row = function(){
    if(!attr.read_only){

      var row = _ts.add();

      if(_mgr.handle_event(_obj, "add_row",
          {
            tabular_section: _tsname,
            grid: _grid,
            row: row,
            wnd: _pwnd.pwnd
          }) === false)
        return;

      setTimeout(function () {
        _grid.selectRowById(row.row);
      }, 100);
    }
  };

  /**
   * удаляет строку табчасти
   */
  _grid._del_row = function(){
    if(!attr.read_only){
      var rId = get_sel_index();

      if(rId != undefined){

        if(_mgr.handle_event(_obj, "del_row", 
            {
              tabular_section: _tsname,
              grid: _grid,
              row: rId,
              wnd: _pwnd.pwnd
            }) === false)
          return;

        _ts.del(rId);

        setTimeout(function () {
          _grid.selectRowById(rId < _ts.count() ? rId + 1 : rId);
        }, 100);
      }
    }
  };
  

  function get_sel_index(silent){
    var selId = _grid.getSelectedRowId();

    if(selId && !isNaN(Number(selId)))
      return Number(selId)-1;

    if(!silent)
      $p.msg.show_msg({
        type: "alert-warning",
        text: $p.msg.no_selected_row.replace("%1", _obj._metadata.tabular_sections[_tsname].synonym || _tsname),
        title: (_obj._metadata.obj_presentation || _obj._metadata.synonym) + ': ' + _obj.presentation
      });
  }

  

  /**
   * обработчик изменения значения примитивного типа
   */
  function tabular_on_edit(stage, rId, cInd, nValue, oValue){

    if(stage != 2 || nValue == oValue)
      return true;

    var cell_field = _grid.get_cell_field(),
      ret_code = _mgr.handle_event(_obj, "value_change", {
        field: cell_field.field,
        value: nValue,
        tabular_section: _tsname,
        grid: _grid,
        row: cell_field.obj,
        cell: (rId && cInd) ? _grid.cells(rId, cInd) : (_grid.getSelectedCellIndex() >=0 ? _grid.cells() : null),
        wnd: _pwnd.pwnd
      });

    if(typeof ret_code !== "boolean"){
      cell_field.obj[cell_field.field] = nValue;
      ret_code = true;
    }
    return ret_code;
  }

  /**
   * наблюдатель за изменениями насбор строк табчасти
   * @param changes
   */
  function observer_rows(changes){
    if(_grid.clearAll){
      changes.some(function(change){
        if (change.type == "rows" && change.tabular == _tsname){
          _ts.sync_grid(_grid, _selection);
          return true;
        }
      });  
    }
  }

  /**
   * наблюдатель за изменениями значений в строках табчасти
   * @param changes
   */
  function observer(changes){
    if(changes.length > 20){
      try{_ts.sync_grid(_grid, _selection);} catch(err){}
    } else
      changes.forEach(function(change){
        if (_tsname == change.tabular){
          if(!change.row || _grid.getSelectedRowId() != change.row.row)
            _ts.sync_grid(_grid, _selection);
          else{
            if(_grid.getColIndexById(change.name) != undefined)
              _grid.cells(change.row.row, _grid.getColIndexById(change.name))
                .setCValue($p.utils.is_data_obj(change.row[change.name]) ? change.row[change.name].presentation : change.row[change.name]);
          }
        }
      });
  }


  // панель инструментов табличной части
  _toolbar.setIconsPath(dhtmlx.image_path + 'dhxtoolbar' + dhtmlx.skin_suffix());
  _toolbar.loadStruct(attr.toolbar_struct || $p.injected_data["toolbar_add_del.xml"], function(){
    
    this.attachEvent("onclick", function(btn_id){

      switch(btn_id) {

        case "btn_add":
          _grid._add_row();
          break;

        case "btn_delete":
          _grid._del_row();
          break;
      }

    });
  });

  // собственно табличная часть
  _grid.setIconsPath(dhtmlx.image_path);
  _grid.setImagePath(dhtmlx.image_path);
  _grid.setHeader(_source.headers);
  if(_source.min_widths)
    _grid.setInitWidths(_source.widths);
  if(_source.min_widths)
    _grid.setColumnMinWidth(_source.min_widths);
  if(_source.aligns)
    _grid.setColAlign(_source.aligns);
  _grid.setColSorting(_source.sortings);
  _grid.setColTypes(_source.types);
  _grid.setColumnIds(_source.fields.join(","));
  _grid.enableAutoWidth(true, 1200, 600);
  _grid.enableEditTabOnly(true);
  _grid.init();

  if(attr.read_only){
    _grid.setEditable(false);
    _toolbar.forEachItem(function (name) {
      if(["btn_add", "btn_delete"].indexOf(name) != -1)
        _toolbar.disableItem(name);
    });
  }

  _grid.__define({

    // TODO: реализовать свойство selection и его инициализацию через attr
    selection: {
      get: function () {
        return _selection;
      },
      set: function (sel) {
        _selection = sel;
        observer_rows([{tabular: _tsname, type: "rows"}]);
      }
    },

    destructor: {
      value: function () {

        if(_obj){
          Object.unobserve(_obj, observer);
          Object.unobserve(_obj, observer_rows);
        }

        _obj = null;
        _ts = null;
        _meta = null;
        _mgr = null;
        _pwnd = null;
        _cell.detachToolbar();

        _destructor.call(_grid);
      }
    },

    get_cell_field: {
      value: function () {

        if(_ts){

          var rindex = get_sel_index(true),
            cindex = _grid.getSelectedCellIndex(),
            row, col;

          if(rindex != undefined){
            row = _ts.get(rindex);

          }else if(_grid._last){
            row = _ts.get(_grid._last.row);
          }

          if(cindex >=0){
            col = _grid.getColumnId(cindex);
          }else if(_grid._last){
            col = _grid.getColumnId(_grid._last.cindex);
          }

          if(row && col){
            return {obj: row, field: col}._mixin(_pwnd);
          }

        }
      }
    },

    refresh_row: {
      value: function (row) {
        _grid.selectRowById(row.row);
        _grid.forEachCell(row.row, function(cellObj,ind){
          var val = row[_grid.getColumnId(ind)];
          cellObj.setCValue($p.utils.is_data_obj(val) ? val.presentation : val);
        });
      }
    }
  });

  _grid.attachEvent("onEditCell", tabular_on_edit);

  _grid.attachEvent("onRowSelect", function(rid,ind){
    if(_ts){
      _grid._last = {
        row: rid-1,
        cindex: ind
      }
    }
  });

  // заполняем табчасть данными
  observer_rows([{tabular: _tsname, type: "rows"}]);

  // начинаем следить за объектом и, его табчастью допреквизитов
  Object.observe(_obj, observer, ["row"]);
  Object.observe(_obj, observer_rows, ["rows"]);

  return _grid;
};