text: (function(root, factory) {
    // Set up Backform appropriately for the environment. Start with AMD.
    if (typeof define === 'function' && define.amd) {
      define([
        'sources/gettext', 'underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'alertify',
        'moment', 'bignumber', 'bootstrap.datetimepicker', 'bootstrap.switch'
      ],
       function(gettext, _, $, Backbone, Backform, Backgrid, Alertify, moment, BigNumber) {
        // Export global even in AMD case in case this script is loaded with
        // others that may still expect a global Backform.
        return factory(root, gettext, _, $, Backbone, Backform, Backgrid, Alertify, moment, BigNumber);
      });
  
    // Next for Node.js or CommonJS. jQuery may not be needed as a module.
    } else if (typeof exports !== 'undefined') {
      var gettext = require('sources/gettext'),
        _ = require('underscore') || root._,
        $ = root.jQuery || root.$ || root.Zepto || root.ender,
        Backbone = require('backbone') || root.Backbone,
        Backform = require('backform') || root.Backform,
        Backgrid = require('backgrid') || root.Backgrid,
        Alertify = require('alertify') || root.Alertify,
        moment = require('moment') || root.moment;
      factory(root, gettext, _, $, Backbone, Backform, Backgrid, Alertify, moment);
  
    // Finally, as a browser global.
    } else {
      factory(root, root.gettext, root._, (root.jQuery || root.Zepto || root.ender || root.$), root.Backbone, root.Backform, root.Alertify, root.moment);
    }
  } (this, function(root, gettext, _, $, Backbone, Backform, Backgrid, Alertify, moment, BigNumber) {
    /*
       * Add mechanism in backgrid to render different types of cells in
       * same column;
     */
  
    // Add new property cellFunction in Backgrid.Column.
    _.extend(Backgrid.Column.prototype.defaults, {cellFunction: undefined});
  
    // Add tooltip to cell if cell content is larger than
    // cell width
    _.extend(Backgrid.Cell.prototype.events, {
      'mouseover': function(e) {
        var $el = $(this.el);
        if($el.text().length > 0 && !$el.attr('title') &&
          ($el.innerWidth() + 1) < $el[0].scrollWidth
        ) {
          $el.attr('title', $.trim($el.text()));
        }
      }
    });
  
    /* Overriding backgrid sort method.
     * As we are getting numeric, integer values as string
     * from server side, but on client side javascript truncates
     * large numbers automatically due to which backgrid was unable
     * to sort numeric values properly in the grid.
     * To fix this issue, now we check if cell type is integer/number
     * convert it into BigNumber object and make comparison to perform sorting.
     */
  
    _.extend(Backgrid.Body.prototype, {
      sort: function (column, direction) {
  
        if (!_.contains(["ascending", "descending", null], direction)) {
          throw new RangeError('direction must be one of "ascending", "descending" or `null`');
        }
  
        if (_.isString(column)) column = this.columns.findWhere({name: column});
  
        var collection = this.collection;
  
        var order;
        if (direction === "ascending") order = -1;
        else if (direction === "descending") order = 1;
        else order = null;
  
        // Get column type and pass it to comparator.
        var col_type = column.get('cell').prototype.className || 'string-cell',
            comparator = this.makeComparator(column.get("name"), order,
                                            order ?
                                            column.sortValue() :
                                            function (model) {
                                              return model.cid.replace('c', '') * 1;
                                            }, col_type);
  
        if (Backbone.PageableCollection &&
            collection instanceof Backbone.PageableCollection) {
  
          collection.setSorting(order && column.get("name"), order,
                                {sortValue: column.sortValue()});
  
          if (collection.fullCollection) {
            // If order is null, pageable will remove the comparator on both sides,
            // in this case the default insertion order comparator needs to be
            // attached to get back to the order before sorting.
            if (collection.fullCollection.comparator == null) {
              collection.fullCollection.comparator = comparator;
            }
            collection.fullCollection.sort();
            collection.trigger("backgrid:sorted", column, direction, collection);
          }
          else collection.fetch({reset: true, success: function () {
            collection.trigger("backgrid:sorted", column, direction, collection);
          }});
        }
        else {
          collection.comparator = comparator;
          collection.sort();
          collection.trigger("backgrid:sorted", column, direction, collection);
        }
  
        column.set("direction", direction);
  
        return this;
      },
      makeComparator: function (attr, order, func, type) {
  
        return function (left, right) {
          // extract the values from the models
  
          var l = func(left, attr), r = func(right, attr), t;
          if (_.isUndefined(l) || _.isUndefined(r)) return;
  
          var types = ['number-cell', 'integer-cell'];
          if (_.include(types, type)) {
            var _l, _r;
            // NaN if invalid number
            try {
              _l = new BigNumber(l);
            } catch(err) {
              _l = NaN;
            }
  
            try {
              _r = new BigNumber(r);
            } catch(err) {
              _r = NaN;
            }
  
            // if descending order, swap left and right
            if (order === 1) t = _l, _l = _r, _r = t;
  
            if (_l.eq(_r))  // If both are equals
              return 0;
            else if (_l.lt(_r)) // If left is less than right
              return -1;
            else
              return 1;
          }
          else {
            // if descending order, swap left and right
            if (order === 1) t = l, l = r, r = t;
  
             // compare as usual
            if (l === r) return 0;
            else if (l < r) return -1;
            return 1;
          }
        };
      }
    });
  
    _.extend(Backgrid.Row.prototype, {
      makeCell: function (column) {
        return new (this.getCell(column))({
          column: column,
          model: this.model
        });
      },
      /*
       * getCell function will check and execute user given cellFunction to get
       * appropriate cell class for current cell being rendered.
       * User provided cellFunction must return valid cell class.
       * cellFunction will be called with context (this) as column and model as
       * argument.
       */
      getCell: function (column) {
        var cf = column.get("cellFunction");
        if (_.isFunction(cf)){
          var cell = cf.apply(column, [this.model]);
          try {
            return Backgrid.resolveNameToClass(cell, "Cell");
          } catch (e) {
            if (e instanceof ReferenceError) {
              // Fallback to column cell.
              return column.get("cell");
            } else {
              throw e; // Let other exceptions bubble up
            }
          }
        } else {
          return column.get("cell");
        }
      }
    });
  
    var ObjectCellEditor = Backgrid.Extension.ObjectCellEditor = Backgrid.CellEditor.extend({
      modalTemplate: _.template([
        '<div class="subnode-dialog" tabindex="1">',
        '    <div class="subnode-body"></div>',
        '</div>'
      ].join("\n")),
      stringTemplate: _.template([
        '<div class="form-group">',
        '  <label class="control-label col-sm-4"><%=label%></label>',
        '  <div class="col-sm-8">',
        '    <input type="text" class="form-control" name="<%=name%>" value="<%=value%>" placeholder="<%=placeholder%>" />',
        '  </div>',
        '</div>'
      ].join("\n")),
      extendWithOptions: function(options) {
        _.extend(this, options);
      },
      render: function () {
        return this;
      },
      postRender: function(model, column) {
        var editor = this,
            el = this.el,
            columns_length = this.columns_length,
            // To render schema directly from Backgrid cell we use columns schema attribute
            schema = this.schema.length ? this.schema : this.column.get('schema');
  
        if (column != null && column.get("name") != this.column.get("name"))
          return false;
  
        if (!_.isArray(schema)) throw new TypeError("schema must be an array");
  
        // Create a Backbone model from our object if it does not exist
        var $dialog = this.createDialog(columns_length);
  
        // Add the Bootstrap form
        var $form = $('<form class="form-dialog"></form>');
        $dialog.find('div.subnode-body').append($form);
  
        // Call Backform to prepare dialog
        var back_el = $dialog.find('form.form-dialog');
  
        this.objectView = new Backform.Dialog({
          el: back_el, model: this.model, schema: schema,
          tabPanelClassName: function() {
            return 'sub-node-form col-sm-12';
          }
        });
  
        this.objectView.render();
  
        return this;
      },
      createDialog: function(noofcol) {
        var $dialog = this.$dialog = $(this.modalTemplate({title: ""})),
            tr = $("<tr>"),
            noofcol = noofcol || 1,
            td = $("<td>", {class: 'editable sortable renderable', style: 'height: auto', colspan: noofcol+2}).appendTo(tr);
  
        this.tr = tr;
  
        // Show the Bootstrap modal dialog
        td.append($dialog.css('display', 'block'));
        this.el.parent('tr').after(tr);
  
        return $dialog;
      },
      save: function() {
        // Retrieve values from the form, and store inside the object model
        this.model.trigger("backgrid:edited", this.model, this.column, new Backgrid.Command({keyCode:13}));
        if (this.tr) {
          this.tr.remove();
        }
  
        return this;
      },
      remove: function() {
        this.objectView.remove();
        Backgrid.CellEditor.prototype.remove.apply(this, arguments);
        if (this.tr) {
          this.tr.remove();
        }
        return this;
      }
    });
  
    var PGSelectCell = Backgrid.Extension.PGSelectCell = Backgrid.SelectCell.extend({
      // It's possible to render an option group or use a
      // function to provide option values too.
      optionValues: function() {
        var res = [],
            opts = _.result(this.column.attributes, 'options');
        _.each(opts, function(o) {
          res.push([o.label, o.value]);
        });
        return res;
      }
    });
  
    var ObjectCell = Backgrid.Extension.ObjectCell = Backgrid.Cell.extend({
      editorOptionDefaults: {
        schema: []
      },
      className: "edit-cell",
      editor: ObjectCellEditor,
      initialize: function(options) {
        Backgrid.Cell.prototype.initialize.apply(this, arguments);
  
        // Pass on cell options to the editor
        var cell = this,
            editorOptions = {};
        _.each(this.editorOptionDefaults, function(def, opt) {
          if (!cell[opt]) cell[opt] = def;
          if (options && options[opt]) cell[opt] = options[opt];
          editorOptions[opt] = cell[opt];
        });
  
        editorOptions['el'] = $(this.el);
        editorOptions['columns_length'] = this.column.collection.length;
        editorOptions['el'].attr('tabindex' , 1);
  
        this.listenTo(this.model, "backgrid:edit", function (model, column, cell, editor) {
          if (column.get("name") == this.column.get("name"))
            editor.extendWithOptions(editorOptions);
        });
      },
      enterEditMode: function () {
        // Notify that we are about to enter in edit mode for current cell.
       // We will check if this row is editable first
        var canEditRow = (!_.isUndefined(this.column.get('canEditRow')) &&
                            _.isFunction(this.column.get('canEditRow'))) ?
                            Backgrid.callByNeed(this.column.get('canEditRow'),
                            this.column, this.model) : true;
        if (canEditRow) {
          // Notify that we are about to enter in edit mode for current cell.
          this.model.trigger("enteringEditMode", [this]);
  
          Backgrid.Cell.prototype.enterEditMode.apply(this, arguments);
          /* Make sure - we listen to the click event */
          this.delegateEvents();
          var editable = Backgrid.callByNeed(this.column.editable(), this.column, this.model);
  
          if (editable) {
            this.$el.html(
              "<i class='fa fa-pencil-square subnode-edit-in-process'></i>"
              );
            this.model.trigger(
              "pg-sub-node:opened", this.model, this
              );
          }
        } else {
            Alertify.alert("This object is not editable by user",
              function(){
                return true;
            });
        }
      },
      render: function(){
          this.$el.empty();
          this.$el.html("<i class='fa fa-pencil-square-o'></i>");
          this.delegateEvents();
          if (this.grabFocus)
            this.$el.focus();
          return this;
      },
      exitEditMode: function() {
        var index = $(this.currentEditor.objectView.el)
          .find('.nav-tabs > .active > a[data-toggle="tab"]').first()
          .data('tabIndex');
        Backgrid.Cell.prototype.exitEditMode.apply(this, arguments);
        this.model.trigger(
            "pg-sub-node:closed", this, index
            );
        this.grabFocus = true;
      },
      events: {
        'click': function(e) {
          if (this.$el.find('i').first().hasClass('subnode-edit-in-process')) {
            // Need to redundantly undelegate events for Firefox
            this.undelegateEvents();
            this.currentEditor.save();
          } else {
            this.enterEditMode.call(this, []);
          }
          e.preventDefault();
        }
      }
    });
  
    var DeleteCell = Backgrid.Extension.DeleteCell = Backgrid.Cell.extend({
        defaults: _.defaults({
          defaultDeleteMsg: gettext('Are you sure you wish to delete this row?'),
          defaultDeleteTitle: gettext('Delete Row')
        }, Backgrid.Cell.prototype.defaults),
  
        /** @property */
        className: "delete-cell",
        events: {
          "click": "deleteRow"
        },
        deleteRow: function (e) {
          e.preventDefault();
          var that = this;
          // We will check if row is deletable or not
          var canDeleteRow = (!_.isUndefined(this.column.get('canDeleteRow')) &&
                              _.isFunction(this.column.get('canDeleteRow')) ) ?
                               Backgrid.callByNeed(this.column.get('canDeleteRow'),
                                this.column, this.model) : true;
          if (canDeleteRow) {
            var delete_msg = !_.isUndefined(this.column.get('customDeleteMsg')) ?
                             this.column.get('customDeleteMsg'): that.defaults.defaultDeleteMsg;
            var delete_title = !_.isUndefined(this.column.get('customDeleteTitle')) ?
                             this.column.get('customDeleteTitle'): that.defaults.defaultDeleteTitle;
            Alertify.confirm(
              delete_title,
              delete_msg,
              function(evt) {
                that.model.collection.remove(that.model);
              },
              function(evt) {
                return true;
              }
            );
          } else {
            Alertify.alert("This object cannot be deleted",
              function(){
                return true;
              }
            );
          }
        },
        initialize: function () {
            Backgrid.Cell.prototype.initialize.apply(this, arguments);
        },
        render: function () {
            this.$el.empty();
            this.$el.html("<i class='fa fa-trash'></i>");
            this.delegateEvents();
            return this;
        }
    });
  
    var CustomHeaderCell = Backgrid.Extension.CustomHeaderCell = Backgrid.HeaderCell.extend({
      initialize: function () {
        // Here, we will add custom classes to header cell
        Backgrid.HeaderCell.prototype.initialize.apply(this, arguments);
        var getClassName = this.column.get('cellHeaderClasses');
        if (getClassName) {
          this.$el.addClass(getClassName);
        }
      }
    });
  
    /**
      SwitchCell renders a Bootstrap Switch in backgrid cell
    */
    $.fn.bootstrapSwitch = jQuery.fn.bootstrapSwitch;
    var SwitchCell = Backgrid.Extension.SwitchCell = Backgrid.BooleanCell.extend({
      defaults: {
        options: _.defaults({
          onText: 'True',
          offText: 'False',
          onColor: 'success',
          offColor: 'default',
          size: 'mini'
          }, $.fn.bootstrapSwitch.defaults)
      },
  
      className: 'switch-cell',
  
      initialize: function() {
        Backgrid.BooleanCell.prototype.initialize.apply(this, arguments);
        this.onChange = this.onChange.bind(this);
      },
  
      enterEditMode: function() {
        this.$el.addClass('editor');
        $(this.$el.find('input')).focus();
      },
  
      exitEditMode: function() {
        this.$el.removeClass('editor');
      },
  
      events: {
        'switchChange.bootstrapSwitch': 'onChange'
      },
  
      onChange: function () {
        var model = this.model,
            column = this.column,
            val = this.formatter.toRaw(this.$input.prop('checked'), model);
  
        // on bootstrap change we also need to change model's value
        model.set(column.get("name"), val);
      },
  
      render: function () {
        var self = this, col = _.defaults(this.column.toJSON(), this.defaults),
            attributes = this.model.toJSON(),
            attrArr = col.name.split('.'),
            name = attrArr.shift(),
            path = attrArr.join('.'),
            model = this.model, column = this.column,
            rawValue = this.formatter.fromRaw(
              model.get(column.get("name")), model
            ),
            editable = Backgrid.callByNeed(col.editable, column, model);
  
        this.undelegateEvents();
  
        this.$el.empty();
  
        this.$el.append(
          $("<input>", {
            tabIndex: -1,
            type: "checkbox"
            }).prop('checked', rawValue).prop('disabled', !editable));
        this.$input = this.$el.find('input[type=checkbox]').first();
  
        // Override BooleanCell checkbox with Bootstrapswitch
        this.$input.bootstrapSwitch(
          _.defaults(
            {'state': rawValue, 'disabled': !editable}, col.options,
            this.defaults.options
            ));
  
        // Listen for Tab key
        this.$el.on('keydown', function(e) {
          var gotoCell;
          if(e.keyCode == 9) {
            // go to Next Cell & if Shift is also pressed go to Previous Cell
            gotoCell = e.shiftKey ? self.$el.prev() : self.$el.next();
          }
  
          if(gotoCell) {
              setTimeout(function() {
                if(gotoCell.hasClass('editable')) {
                  e.preventDefault();
                  e.stopPropagation();
                  var command = new Backgrid.Command({
                        key: "Tab", keyCode: 9,
                        which: 9, shiftKey: e.shiftKey
                      });
                  self.model.trigger("backgrid:edited", self.model,
                                      self.column, command);
                  gotoCell.focus();
                }
              }, 20);
          }
  
        });
  
        this.delegateEvents();
  
        return this;
      }
    });
  
    /*
     *  Select2Cell for backgrid.
     */
    var Select2Cell = Backgrid.Extension.Select2Cell =
        Backgrid.SelectCell.extend({
      className: "select2-cell",
  
      /** @property */
      editor: null,
  
      defaults: _.defaults({
        select2: {},
        opt: {
          label: null,
          value: null,
          selected: false
         }
        }, Backgrid.SelectCell.prototype.defaults),
  
      enterEditMode: function() {
        if (!this.$el.hasClass('editor'))
          this.$el.addClass('editor');
        this.$select.select2('focus');
        this.$select.select2('open');
        this.$select.on('blur', this.exitEditMode);
      },
  
      exitEditMode: function() {
        this.$select.off('blur', this.exitEditMode);
        this.$select.select2('close');
        this.$el.removeClass('editor');
      },
  
      events: {
        "select2:open": "enterEditMode",
        "select2:close": "exitEditMode",
        "change": "onSave",
        "select2:unselect": "onSave"
      },
      /** @property {function(Object, ?Object=): string} template */
      template: _.template([
        '<option value="<%- value %>" ',
        '<%= selected ? \'selected="selected"\' : "" %>>',
        '<%- label %></option>'].join(''),
        null,{
          variable: null
        }),
  
      initialize: function() {
        Backgrid.SelectCell.prototype.initialize.apply(this, arguments);
        this.onSave = this.onSave.bind(this);
        this.enterEditMode = this.enterEditMode.bind(this);
        this.exitEditMode = this.exitEditMode.bind(this);
      },
  
      render: function () {
        var col = _.defaults(this.column.toJSON(), this.defaults),
            model = this.model, column = this.column,
            editable = Backgrid.callByNeed(col.editable, column, model),
            optionValues = _.clone(this.optionValues ||
                  (_.isFunction(this.column.get('options')) ?
                      (this.column.get('options'))(this) :
                      this.column.get('options')));
  
        this.undelegateEvents();
  
        if (this.$select) {
          if ( this.$select.data('select2')) {
            this.$select.select2('destroy');
          }
          delete this.$select;
          this.$select = null;
        }
  
        this.$el.empty();
  
        if (!_.isArray(optionValues))
          throw new TypeError("optionValues must be an array");
  
        /*
         * Add empty option as Select2 requires any empty '<option><option>' for
         * some of its functionality to work.
         */
        optionValues.unshift(this.defaults.opt);
  
        var optionText = null,
            optionValue = null,
            self = this,
            model = this.model,
            selectedValues = model.get(this.column.get("name")),
            select2_opts = _.extend(
              {openOnEnter: false, multiple:false}, self.defaults.select2,
              (col.select2 || {})
              ),
            selectTpl = _.template('<select <%=multiple ? "multiple" : "" %>></select>');
  
        var $select = self.$select = $(selectTpl({
          multiple: select2_opts.multiple
        })).appendTo(self.$el);
  
        for (var i = 0; i < optionValues.length; i++) {
          var opt = optionValues[i];
  
          if (_.isArray(opt)) {
  
            optionText  = opt[0];
            optionValue = opt[1];
  
            $select.append(
              self.template({
                 label: optionText,
                 value: optionValue,
                 selected: (selectedValues == optionValue) ||
                   (select2_opts.multiple && _.indexOf(selectedValues, optionValue) > -1)
              }));
           } else {
            opt = _.defaults({}, opt, {
              selected: ((selectedValues == opt.value) ||
                  (select2_opts.multiple && _.indexOf(selectedValues, opt.value) > -1)),
              }, self.defaults.opt);
            $select.append(self.template(opt));
          }
        }
  
        if(col && _.has(col.disabled)) {
          _.extend(select2_opts, {
            disabled: evalF(col.disabled, col, model)
          });
        } else {
          _.extend(select2_opts, {disabled: !editable});
        }
  
        this.delegateEvents();
  
        // If disabled then no need to show placeholder
        if(!editable || col.mode === 'properties') {
          select2_opts['placeholder'] = '';
        }
  
        // Initialize select2 control.
        this.$sel = this.$select.select2(select2_opts);
  
        // Select the highlighted item on Tab press.
        if (this.$sel) {
          this.$sel.data('select2').on("keypress", function(ev) {
            var self = this;
  
            // keycode 9 is for TAB key
            if (ev.which === 9 && self.isOpen()) {
              self.trigger('results:select', {});
              ev.preventDefault();
            }
          });
        }
  
        return this;
      },
  
      /**
         Saves the value of the selected option to the model attribute.
      */
      onSave: function (e) {
        var model = this.model;
        var column = this.column;
  
        model.set(column.get("name"), this.$select.val());
      },
  
      remove: function() {
        this.$select.off('change', this.onSave);
        if (this.$select.data('select2')) {
          this.$select.select2('destroy');
        }
        this.$el.empty();
        Backgrid.SelectCell.prototype.remove.apply(this, arguments);
      }
    });
  
    /**
      TextareaCellEditor the cell editor renders a textarea multi-line text input
      box as its editor.
  
      @class Backgrid.TextareaCellEditor
      @extends Backgrid.InputCellEditor
    */
    var TextareaCellEditor = Backgrid.TextareaCellEditor = Backgrid.InputCellEditor.extend({
      /** @property */
      tagName: "textarea",
  
      events: {
        "blur": "saveOrCancel",
        "keydown": ""
      }
    });
  
    /**
      TextareaCell displays multiline HTML strings.
  
        @class Backgrid.Extension.TextareaCell
        @extends Backgrid.Cell
    */
    var TextareaCell = Backgrid.Extension.TextareaCell = Backgrid.Cell.extend({
      /** @property */
      className: "textarea-cell",
  
      editor: TextareaCellEditor
    });
  
  
    /**
     * Custom header icon cell to add the icon in table header.
    */
    var CustomHeaderIconCell = Backgrid.Extension.CustomHeaderIconCell = Backgrid.HeaderCell.extend({
        /** @property */
        className: "header-icon-cell",
        events: {
          "click": "addHeaderIcon"
        },
        addHeaderIcon: function (e) {
          var self = this,
          m = new (this.collection.model);
          this.collection.add(m)
          e.preventDefault();
        },
        render: function () {
            this.$el.empty();
            //this.$el.html("<i class='fa fa-plus-circle'></i>");
            this.$el.html("<label><a><span style='font-weight:normal;'>Array Values</a></span></label> <button class='btn-sm btn-default add'>Add</button>");
            this.delegateEvents();
            return this;
        }
    });
  
  
    var arrayCellModel = Backbone.Model.extend({
      defaults: {
          value: undefined
      }
    });
  
    /**
       Custom InputArrayCellEditor for editing user input array for debugger.
     */
    var InputArrayCellEditor = Backgrid.Extension.InputArrayCellEditor =
      Backgrid.CellEditor.extend({
        tagName: "div",
  
      events: {
        'blur': 'lostFocus'
      },
  
      render: function () {
          var self = this,
              arrayValuesCol = this.model.get(this.column.get("name")),
              tbl = $("<table></table>").appendTo(this.$el),
              gridCols = [
                      {name: 'value', label:'Array Values', type: 'text', cell:'string', headerCell: Backgrid.Extension.CustomHeaderIconCell, cellHeaderClasses: 'width_percent_100'},
                      ],
              gridBody = $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>");
  
          this.$el.attr('tabindex', '1');
  
          gridCols.unshift({
            name: "pg-backform-delete", label: "",
            cell: Backgrid.Extension.DeleteCell,
            //headerCell: Backgrid.Extension.CustomHeaderIconCell,
            editable: false, cell_priority: -1
          });
  
        this.$el.empty();
        var grid = self.grid = new Backgrid.Grid({
              columns: gridCols,
              collection:arrayValuesCol
            });
  
        grid.render();
  
        gridBody.append(grid.$el)
  
        this.$el.html(gridBody);
  
        $(self.$el).pgMakeVisible('backform-tab');
        self.delegateEvents();
  
        return this;
      },
  
      /*
       * Call back function when the grid lost the focus
       */
      lostFocus: function(ev) {
  
        var self = this,
        /*
         * Function to determine whether one dom element is descendant of another
         * dom element.
         */
        isDescendant = function (parent, child) {
           var node = child.parentNode;
           while (node != null) {
               if (node == parent) {
                   return true;
               }
               node = node.parentNode;
           }
           return false;
        }
        /*
         * Between leaving the old element focus and entering the new element focus the
         * active element is the document/body itself so add timeout to get the proper
         * focused active element.
         */
        setTimeout(function() {
          if (self.$el[0] != document.activeElement && !isDescendant(self.$el[0], document.activeElement)){
            var m = self.model,
              column = self.column;
            m.trigger('backgrid:edited', m, column, new Backgrid.Command(ev));
  
            setTimeout(function(){
              if (self.grid) {
                  self.grid.remove();
                  self.grid = null;
              }
            }, 10);
  
  
          } },10);
        return;
      }
    });
  
    /*
     * This will help us transform the user input string array values in proper format to be
     * displayed in the cell.
     */
    var InputStringArrayCellFormatter = Backgrid.Extension.InputStringArrayCellFormatter =
      function () {};
    _.extend(InputStringArrayCellFormatter.prototype, {
      /**
       * Takes a raw value from a model and returns an optionally formatted
       * string for display.
       */
      fromRaw: function (rawData, model) {
          var values = []
          rawData.each(function(m){
              var val = m.get('value');
              if (_.isUndefined(val)) {
                  values.push("null");
              } else {
                  values.push(m.get("value"));
              }
            }
          );
          return values.toString();
      },
      toRaw: function (formattedData, model) {
        return formattedData;
      }
    });
  
    /*
     * This will help us transform the user input integer array values in proper format to be
     * displayed in the cell.
     */
    var InputIntegerArrayCellFormatter = Backgrid.Extension.InputIntegerArrayCellFormatter =
      function () {};
    _.extend(InputIntegerArrayCellFormatter.prototype, {
      /**
       * Takes a raw value from a model and returns an optionally formatted
       * string for display.
       */
      fromRaw: function (rawData, model) {
          var values = []
          rawData.each(function(m){
              var val = m.get('value');
              if (_.isUndefined(val)) {
                  values.push("null");
              } else {
                  values.push(m.get("value"));
              }
            }
          );
          return values.toString();
      },
      toRaw: function (formattedData, model) {
          formattedData.each(function(m){
              m.set("value", parseInt(m.get('value')), {silent: true});
          });
  
        return formattedData;
      }
    });
  
    /*
     *  InputStringArrayCell for rendering and taking input for string array type in debugger
     */
    var InputStringArrayCell = Backgrid.Extension.InputStringArrayCell = Backgrid.Cell.extend({
      className: "width_percent_25",
      formatter: InputStringArrayCellFormatter,
      editor: InputArrayCellEditor,
  
      initialize: function() {
        Backgrid.Cell.prototype.initialize.apply(this, arguments);
          // set value to empty array.
          var m = arguments[0].model;
          if (_.isUndefined(this.collection)) {
              this.collection = new (Backbone.Collection.extend({
              model: arrayCellModel}))(m.get('value'));
          }
  
          this.model.set(this.column.get('name'), this.collection);
  
          this.listenTo(this.collection, "remove", this.render);
      },
    });
  
    /*
     *  InputIntegerArrayCell for rendering and taking input for integer array type in debugger
     */
    var InputIntegerArrayCell = Backgrid.Extension.InputIntegerArrayCell = Backgrid.Cell.extend({
      className: "width_percent_25",
      formatter: InputIntegerArrayCellFormatter,
      editor: InputArrayCellEditor,
  
      initialize: function() {
        Backgrid.Cell.prototype.initialize.apply(this, arguments);
          // set value to empty array.
          var m = arguments[0].model;
          if (_.isUndefined(this.collection)) {
              this.collection = new (Backbone.Collection.extend({
              model: arrayCellModel}))(m.get('value'));
          }
  
  
          this.model.set(this.column.get('name'),this.collection);
  
          this.listenTo(this.collection, "remove", this.render);
      },
    });
  
    /**
     * DependentCell functions can be used with the different cell type in order
     * to setup the callback for the depedent attribute change in the model.
     *
     * Please implement the 'dependentChanged' as the callback in the used cell.
     *
     * @class Backgrid.Extension.DependentCell
     **/
    var DependentCell = Backgrid.Extension.DependentCell = function() {};
  
    _.extend(
      DependentCell.prototype, {
        initialize: function(){
          // Listen to the dependent fields in the model for any change
          var deps = this.column.get('deps');
          var self = this;
  
          if (deps && _.isArray(deps)) {
            _.each(deps, function(d) {
              var attrArr = d.split('.'),
              name = attrArr.shift();
              self.listenTo(self.model, "change:" + name, self.dependentChanged);
            });
          }
        },
        remove: function() {
          // Remove the events for the dependent fields in the model
          var self = this,
            deps = self.column.get('deps');
  
          if (deps && _.isArray(deps)) {
            _.each(deps, function(d) {
              var attrArr = d.split('.'),
                name = attrArr.shift();
  
              self.stopListening(self.model, "change:" + name, self.dependentChanged);
            });
          }
        }
      });
  
    /**
     Formatter for PasswordCell.
  
     @class Backgrid.PasswordFormatter
     @extends Backgrid.CellFormatter
     @constructor
    */
    var PasswordFormatter = Backgrid.PasswordFormatter = function () {};
    PasswordFormatter.prototype = new Backgrid.CellFormatter();
    _.extend(PasswordFormatter.prototype, {
      fromRaw: function (rawValue, model) {
  
        if (_.isUndefined(rawValue) || _.isNull(rawValue)) return '';
  
        var pass = '';
        for(var i = 0; i < rawValue.length; i++) {
          pass += '*';
        }
        return pass;
      }
    });
  
    var PasswordCell = Backgrid.Extension.PasswordCell  = Backgrid.StringCell.extend({
  
      formatter: PasswordFormatter,
  
      editor: Backgrid.InputCellEditor.extend({
        attributes: {
          type: "password"
        },
  
        render: function () {
          var model = this.model;
          this.$el.val(model.get(this.column.get("name")));
          return this;
        }
      })
    });
  
    /*
     * Override NumberFormatter to support NaN, Infinity values.
     * On client side, JSON do not directly support NaN & Infinity,
     * we explicitly converted it into string format at server side
     * and we need to parse it again in float at client side.
     */
    _.extend(Backgrid.NumberFormatter.prototype, {
      fromRaw: function (number, model) {
        if (_.isNull(number) || _.isUndefined(number)) return '';
  
        number = parseFloat(number).toFixed(~~this.decimals);
  
        var parts = number.split('.');
        var integerPart = parts[0];
        var decimalPart = parts[1] ? (this.decimalSeparator || '.') + parts[1] : '';
  
        return integerPart.replace(this.HUMANIZED_NUM_RE, '$1' + this.orderSeparator) + decimalPart;
      }
    });
  
    /*
     *  JSONBCell Formatter.
     */
    var JSONBCellFormatter = Backgrid.Extension.JSONBCellFormatter =
      function () {};
    _.extend(JSONBCellFormatter.prototype, {
      fromRaw: function (rawData, model) {
          // json data
          if(_.isArray(rawData)) {
            var converted_data = '';
            converted_data = _.map(rawData, function(data) {
              return JSON.stringify(JSON.stringify(data));
            });
            return '{' + converted_data.join() + '}';
          } else if(_.isObject(rawData)) {
            return JSON.stringify(rawData);
          } else {
            return rawData;
          }
      },
      toRaw: function (formattedData, model) {
        return formattedData;
      }
    });
  
    /*
     *  JSONBCell for backgrid.
     */
    var JSONBCell = Backgrid.Extension.JSONBCell =
        Backgrid.StringCell.extend({
          className: "jsonb-cell",
          formatter: JSONBCellFormatter
        });
  
    var DatepickerCell = Backgrid.Extension.DatepickerCell = Backgrid.Cell.extend({
      editor: DatepickerCellEditor
    });
  
    var DatepickerCellEditor = Backgrid.InputCellEditor.extend({
      events:{},
      initialize:function() {
        Backgrid.InputCellEditor.prototype.initialize.apply(this, arguments);
        var input = this;
        $(this.el).prop('readonly', true);
        $(this.el).datepicker({
          onClose: function(newValue){
            var command = new Backgrid.Command({});
            input.model.set(input.column.get("name"), newValue);
            input.model.trigger(
              "backgrid:edited", input.model, input.column, command
            );
            command = input = null;
          }
        });
      }
    });
  
    // Reference:
    // https://github.com/wyuenho/backgrid-moment-cell/blob/master/backgrid-moment-cell.js
    /**
     MomentFormatter converts bi-directionally any datetime values in any format
     supported by [moment()](http://momentjs.com/docs/#/parsing/) to any
     datetime format
     [moment.fn.format()](http://momentjs.com/docs/#/displaying/format/)
     supports.
     @class Backgrid.Extension.MomentFormatter
     @extends Backgrid.CellFormatter
     @constructor
     */
    var MomentFormatter = Backgrid.Extension.MomentFormatter = function (options) {
      _.extend(this, this.defaults, options);
    };
  
    MomentFormatter.prototype = new Backgrid.CellFormatter;
    _.extend(MomentFormatter.prototype, {
      /**
         @cfg {Object} options
         @cfg {boolean} [options.modelInUnixOffset=false] Whether the model values
         should be read/written as the number of milliseconds since UNIX Epoch.
         @cfg {boolean} [options.modelInUnixTimestamp=false] Whether the model
         values should be read/written as the number of seconds since UNIX Epoch.
         @cfg {boolean} [options.modelInUTC=true] Whether the model values should
         be read/written in UTC mode or local mode.
         @cfg {string} [options.modelLang=moment.locale() moment>=2.8.0 |
         moment.lang() moment<2.8.0] The locale the model values should be
         read/written in.
         @cfg {string} [options.modelFormat=moment.defaultFormat] The format this
         moment formatter should use to read/write model values. Only meaningful if
         the values are strings.
         @cfg {boolean} [options.displayInUnixOffset=false] Whether the display
         values should be read/written as the number of milliseconds since UNIX
         Epoch.
         @cfg {boolean} [options.displayInUnixTimestamp=false] Whether the display
         values should be read/written as the number of seconds since UNIX Epoch.
         @cfg {boolean} [options.displayInUTC=true] Whether the display values
         should be read/written in UTC mode or local mode.
         @cfg {string} [options.displayLang=moment.locale() moment>=2.8.0 |
         moment.lang() moment<2.8.0] The locale the display values should be
         read/written in.
         @cfg {string} [options.displayFormat=moment.defaultFormat] The format
         this moment formatter should use to read/write dislay values.
         */
      defaults: {
        modelInUnixOffset: false,
        modelInUnixTimestamp: false,
        modelInUTC: true,
        modelLang: moment.locale(),
        modelFormat: moment.defaultFormat,
        displayInUnixOffset: false,
        displayInUnixTimestamp: false,
        displayInUTC: true,
        displayLang: moment.locale(),
        displayFormat: moment.defaultFormat,
        allowEmpty: false
      },
  
      /**
         Converts datetime values from the model for display.
         @member Backgrid.Extension.MomentFormatter
         @param {*} rawData
         @return {string}
         */
      fromRaw: function (rawData) {
        if (rawData == null) return '';
  
        var m = this.modelInUnixOffset ? moment(rawData) :
          this.modelInUnixTimestamp ? moment.unix(rawData) :
          this.modelInUTC ?
          moment.utc(rawData, this.modelFormat, this.modelLang) :
          moment(rawData, this.modelFormat, this.modelLang);
  
        if (this.displayInUnixOffset) return +m;
  
        if (this.displayInUnixTimestamp) return m.unix();
  
        if (this.displayLang) m.locale(this.displayLang);
  
        if (this.displayInUTC) m.utc(); else m.local();
  
        if (this.displayFormat != moment.defaultFormat) {
          return m.format(this.displayFormat);
        }
  
        return m.format();
      },
  
      /**
         Converts datetime values from user input to model values.
         @member Backgrid.Extension.MomentFormatter
         @param {string} formattedData
         @return {string}
         */
      toRaw: function (formattedData) {
  
        var m = this.displayInUnixOffset ? moment(+formattedData) :
          this.displayInUnixTimestamp ? moment.unix(+formattedData) :
          this.displayInUTC ?
          moment.utc(formattedData, this.displayFormat, this.displayLang) :
          moment(formattedData, this.displayFormat, this.displayLang);
  
        if (!m || !m.isValid()) return (this.allowEmpty && formattedData === '') ? null : undefined;
  
        if (this.modelInUnixOffset) return +m;
  
        if (this.modelInUnixTimestamp) return m.unix();
  
        if (this.modelLang) m.locale(this.modelLang);
  
        if (this.modelInUTC) m.utc(); else m.local()
  
        if (this.modelFormat != moment.defaultFormat) {
          return m.format(this.modelFormat);
        }
  
        return m.format();
      }
    });
  
    var MomentCell = Backgrid.Extension.MomentCell = Backgrid.Cell.extend({
  
      editor: Backgrid.InputCellEditor,
  
      /** @property */
      className: "datetime-cell",
  
      /** @property {Backgrid.CellFormatter} [formatter=Backgrid.Extension.MomentFormatter] */
      formatter: MomentFormatter,
  
      /**
         Initializer. Accept Backgrid.Extension.MomentFormatter.options and
         Backgrid.Cell.initialize required parameters.
       */
      initialize: function (options) {
  
        MomentCell.__super__.initialize.apply(this, arguments);
  
        var formatterDefaults = MomentFormatter.prototype.defaults;
        var formatterDefaultKeys = _.keys(formatterDefaults);
        var classAttrs = _.pick(this, formatterDefaultKeys);
        var formatterOptions = _.pick(options, formatterDefaultKeys);
        var columnsAttrs = _.pick(this.column.toJSON(), formatterDefaultKeys);
  
        // Priority of the options for the formatter, from highest to lowerest
        // 1. MomentCell instance options
        // 2. MomentCell class attributes
        // 3. MomentFormatter defaults
  
        // this.formatter will have been instantiated now
        _.extend(this.formatter, formatterDefaults, classAttrs, formatterOptions, columnsAttrs);
  
        this.editor = this.editor.extend({
          attributes: _.extend({}, this.editor.prototype.attributes || this.editor.attributes || {}, {
            placeholder: this.formatter.displayFormat
          }),
          options: this.column.get('options')
        });
      }
    });
  
    var DatetimePickerEditor = Backgrid.Extension.DatetimePickerEditor = Backgrid.InputCellEditor.extend({
      postRender: function() {
        var self = this,
            evalF = function() {
              var args = [];
              Array.prototype.push.apply(args, arguments);
              var f = args.shift();
  
              if (typeof(f) === 'function') {
                return f.apply(self, args);
              }
              return f;
            },
            options = _.extend({
              format: "YYYY-MM-DD HH:mm:ss Z",
              showClear: true,
              showTodayButton: true,
              toolbarPlacement: 'top'
            }, evalF(this.column.get('options')), {
              keyBinds: {
                "shift tab": function(widget) {
                  if (widget) {
                    // blur the input
                    setTimeout(
                      function() {
                        self.closeIt({keyCode: 9, shiftKey: true});
                      }, 10
                    );
                  }
                },
                tab: function(widget) {
                  if (widget) {
                    // blur the input
                    setTimeout(
                      function() {
                        self.closeIt({keyCode: 9});
                      }, 10
                    );
                  }
                }
              }
            });
        this.$el.datetimepicker(options);
        this.$el.datetimepicker('show');
        this.picker = this.$el.data('DateTimePicker');
      },
      events: {
        'dp.hide': 'closeIt'
      },
      closeIt: function(ev) {
        var formatter = this.formatter,
            model = this.model,
            column = this.column,
            val = this.$el.val(),
            newValue = formatter.toRaw(val, model);
  
        if (this.is_closing)
          return;
        this.is_closing = true;
        this.$el.datetimepicker('destroy');
        this.is_closing = false;
  
        var command = new Backgrid.Command(ev);
  
        if (_.isUndefined(newValue)) {
          model.trigger("backgrid:error", model, column, val);
        } else {
          model.set(column.get("name"), newValue);
          model.trigger("backgrid:edited", model, column, command);
        }
      }
    });
  
    _.extend(MomentCell.prototype, MomentFormatter.prototype.defaults);
  
  
    Backgrid.Extension.StringDepCell = Backgrid.StringCell.extend({
        initialize: function() {
          Backgrid.StringCell.prototype.initialize.apply(this, arguments);
          Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
        },
        dependentChanged: function () {
          this.$el.empty();
  
          var self = this,
              model = this.model,
              column = this.column,
              editable = this.column.get("editable");
  
          this.render();
  
          var is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
          setTimeout(function() {
            self.$el.removeClass("editor");
            if (is_editable){ self.$el.addClass("editable"); }
            else { self.$el.removeClass("editable"); }
          }, 10);
  
          this.delegateEvents();
          return this;
        },
        remove: Backgrid.Extension.DependentCell.prototype.remove
      });
  
    Backgrid.Extension.Select2DepCell = Backgrid.Extension.Select2Cell.extend({
      initialize: function() {
        Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
      },
  
      dependentChanged: function () {
        var model = this.model;
        var column = this.column;
        editable = this.column.get("editable");
  
        this.render();
  
        is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
        if (is_editable){ this.$el.addClass("editable"); }
        else { this.$el.removeClass("editable"); }
  
        this.delegateEvents();
        return this;
      },
      remove: Backgrid.Extension.DependentCell.prototype.remove
    });
  
    return Backgrid;
  
  }));
  