gridstack-8.x-2.5/js/admin/gridstack.admin.backbone.crud.js

js/admin/gridstack.admin.backbone.crud.js
/**
 * @file
 * Provides GridStack Backbone.Crud.
 */

(function ($, Drupal, Backbone, _, _db) {

  'use strict';

  Drupal.gridstack = Drupal.gridstack || {};
  _.each(['base', 'models', 'views'], function (key) {
    Drupal.gridstack[key] = Drupal.gridstack[key] || {};
  });

  /**
   * The GridStack nested methods.
   */
  Drupal.gridstack.nested = {

    onNestedRemoveMultiple: function (e, box, $box) {
      var me = this;
      var delta = $box.index();

      $box = $box.length ? $box : $($box);
      box = me.isValidModel(box) ? box : me.collection.at(delta);

      // Content box is not a model, no need to remove box model.
      me.removeWidget($box, 'nested');

      // Manually trigger change since nested are not backbone models.
      me.collection.trigger('change', box);
    },

    onNestedAddMultiple: function (e, box, $box) {
      var me = this;
      var delta = $box.index();
      box = me.isValidModel(box) ? box : me.collection.at(delta);
      me.isRendered = true;

      if (me.isValidModel(box)) {
        var el = $box.find('.gridstack:first');
        if (el.length) {
          me.addNestedWidget(box, el, true);
        }

        // Manually trigger change since nested are not backbone models.
        me.collection.trigger('change', box);
      }
    },

    // See ::addWidget(node, el, isDefault, gridStack)
    addNestedWidget: function (box, el, isDefault, data) {
      var me = this;
      var index = box.get('index');
      var gridStack = GridStack.init(me.nestedOptions, el);
      var len = gridStack.engine.nodes.length || 0;

      var addNestedItem = function (node, i) {
        node = node || box.defaults;
        var indexNested = isDefault ? len + 1 : i + 1;

        // Nested box is not a backbone model, only has similar attributes.
        node = me.getFakeModel(node, index, indexNested);
        var $box = me.addWidget(node, el, isDefault, gridStack);
        me.updateBoxNested($box, node);
      };

      if (isDefault) {
        addNestedItem();
      }
      else if (data.length) {
        gridStack.batchUpdate();

        _.each(data, function (node, i) {
          addNestedItem(node, i);
        }, this);

        gridStack.commit();
      }

      me.collection.trigger('change', box);
      // Nested boxes are not backbone models, hook into gridstack events.
      // @todo no need gridStack.on('added', _.bind(me.onNestedAdd, me));
      return gridStack;
    },

    addNestedWidgetMultiple: function (box, i) {
      var me = this;
      var data = _.isUndefined(me.nestedNodes[i]) ? [] : me.nestedNodes[i];
      var id = box.get('id');
      var el = $('> .box[data-gs-bid="' + id + '"] .gridstack:first', me.$el);

      return el.length ? me.addNestedWidget(box, el, false, data) : null;
    },

    updateBoxNestedMultiple: function (box, $box) {
      var me = this;

      $box = $box.length ? $box : $($box);
      var delta = $box.index();

      box = me.isValidModel(box) ? box : me.collection.at(delta);
      if (!me.isValidModel(box)) {
        return;
      }

      var index = box.get('index');
      var nested = [];
      var $nested = $('.gridstack:first', $box);
      var $boxes = $('> .box:not(.gridstack__box--placeholder)', $nested);

      // Might be empty, even if not empty here due to sequence, or delay.
      if ($boxes.length) {
        _.each($boxes, function (el, i) {
          var $el = $(el);
          var node = $el.data('_gridstack_node');

          if (me.isValid(node)) {
            var data = me.getFakeModel(node, index, (i + 1));
            nested.push(data);

            me.updateBoxNested($el, data);
          }
        });
      }

      box.set('nested', nested, {silent: false});
    },

    updateBoxNested: function ($box, node) {
      $box = $box.length ? $box : $($box);
      var indexNested = node.indexNested || ($box.index() + 1);

      $box.attr('data-gs-bid', node.id);
      $box.attr('data-gs-index', indexNested);
      $box.addClass('is-box-nested');

      $box.find('.btn').attr('data-gs-bid', node.id);
      if (!_.isUndefined(node.mid)) {
        $box.find('.btn').attr('data-gs-mid', node.mid);
      }
    }

  };

  /**
   * The GridStack crud methods.
   */
  Drupal.gridstack.crud = _.extend(Drupal.gridstack.nested, {
    events: {
      resizestop: 'onResizeStop',
      drag: 'onDrag'
    },

    onChange: function () {
      this.save();
    },

    onSave: function () {
      this.options.updateIcon = true;
      this.save();
    },

    onDragResize: function (e) {
      var me = this;
      var $box = $(e.target);
      var box = me.getModel($box);

      // This is GridStack library selector.
      if (me.$el[0] === e.delegateTarget) {
        // Only a nested box needs manual update.
        // @todo move it to view along with the model if you can.
        if ($box.hasClass('is-box-nested')) {
          me.updateBoxDimensions($box);
        }
        else {
          var node = $box.data('_gridstack_node');
          if (me.isValidModel(box) && me.isValidNode(node)) {
            box.set('width', node.width, {silent: false});
            box.set('height', node.height, {silent: false});
          }
        }
      }

      // Manually trigger change since this event is not backbone's.
      me.collection.trigger('change', box);
    },

    onDrag: function (e) {
      _db.throttle(this.onDragResize(e), 200, this);
    },

    onResizeStop: function (e) {
      _db.throttle(this.onDragResize(e), 200, this);
    },

    onAdd: function (box) {
      this.addWidget(box, null, true);
    },

    widgetOptions: function (node, extra) {
      // v0.5.2, https://github.com/gridstack/gridstack.js/issues/907
      return _.extend({
        x: node.x,
        y: node.y,
        width: node.width,
        height: node.height,
        // Without autoPosition, new widgets are prepended, not appended.
        autoPosition: true,
        // Dont! The box can not get smaller than 12 columns.
        // minWidth: 1
        // This can get absurd to 225+, add a max.
        // minHeight: 1,
        maxHeight: 12
      }, extra || {});
    },

    widgetHtml: function (box, el, type) {
      var me = this;
      if (type === 'root') {
        var view = me.getCurrentView(box);
        if (me.isValid(view)) {
          return view.render().el.outerHTML;
        }
      }
      // Disable infinite nests.
      return Drupal.theme('gridStackBox', {
        isNested: me.isNested && !el.hasClass('gridstack--nested')
      });
    },

    addWidget: function (box, el, isDefault, gridStack) {
      var me = this;
      var node = box;
      var extra = {};
      var widget;
      var isFakeModel = false;

      el = el || me.$el;
      isDefault = isDefault || false;

      gridStack = gridStack || me.getGridStack(el);
      if (!me.isValid(gridStack)) {
        return;
      }

      // Unlike nested, the root boxes are valid backbone models.
      if (me.isValidModel(box)) {
        // @todo, not functional, yet. For future nested models.
        box.set('gid', me.$el.attr('id'), {silent: true});
        node = box.attributes;
        if (me.isNested) {
          extra.minWidth = 12;
        }

        if (me.column < 12 && me.column > 4) {
          extra.maxWidth = me.column;
        }

        widget = me.widgetHtml(box, el, 'root');
      }
      else {
        isFakeModel = true;
        widget = me.widgetHtml(box, el, 'nested');
      }

      if (isDefault) {
        node.height = 2;
        node.width = 2;
      }

      if (!gridStack.willItFit(node.x, node.y, node.width, node.height, true)) {
        return;
      }

      widget = gridStack.addWidget(widget, me.widgetOptions(node, extra));
      if (!_.isUndefined(el) && _.isUndefined(widget.context)) {
        widget.context = el.length ? el[0] : el;
      }

      // The ::addWidget returns non-jquery object without length.
      var $box = $(widget);
      node = $box.data('_gridstack_node') || node;

      if (isFakeModel) {
        // Cannot move it to model, _yet, since this is rendered later.
        me.updateBoxDimensions($box);
      }

      // Disable movable, as too complex to handle with breakpoints for now.
      if (me.isNested && $box.parent('.gridstack--root').length) {
        gridStack.movable($box[0], false);
      }

      // Must trigger manually to apply attributes on added widgets.
      me.collection.trigger('change', box);
      return $box;
    },

    onRemove: function (box) {
      var me = this;
      var view = me.getCurrentView(box);

      if (me.isValid(view)) {
        view.remove();
        view.stopListening(box);
      }
    },

    shouldRemove: function (box) {
      return box.get('deleted') === true;
    },

    removeWidget: function ($box, context) {
      var me = this;

      $box = $box.length ? $box : $($box);
      context = context === 'undefined' ? 'root' : context;

      if (!$box.length) {
        return;
      }

      var box = me.getModel($box);
      var el = $box.closest('.gridstack');

      if (me.isValidModel(box) && context === 'root') {
        // Do not remove directly, instead set properties, and collect it later
        // to avoid potential memory leaks.
        box.set('deleted', true);
      }

      // @todo move it model if you can.
      var gridStack = me.getGridStack(el);
      if (me.isValid(gridStack)) {
        gridStack.removeWidget($box, true);
      }
    },

    onRootRemoveMultiple: function (e, box, $box) {
      var me = this;

      $box = $box.length ? $box : $($box);

      // Destroy the box including all nested instances.
      me.removeWidget($box, 'root');
    },

    onChangeImageStyle: function (e) {
      var me = this;
      var $el = $(e.currentTarget);
      var $box = $el.closest('.box');
      var i = $box.index();
      var box = me.collection.at(i);
      var stored = me.getStoredImageStyle(i);
      var v = $el.val() || stored || $box.data('imageStyle');

      $el.val(v).attr('data-imageid', v);
      $el.find('option:selected').prop('selected', true).siblings('option').prop('selected', false);

      // Pass it to Backbone model.
      if (me.isValidModel(box)) {
        box.set('image_style', v);
      }

      if (me.isRendered) {
        me.collection.trigger('change', box);
      }
    },

    onClickImageStyle: function (e) {
      var $el = $(e.currentTarget);
      var $box = $el.closest('.box');
      var v = $box.data('imageStyle');

      if ($el.children().length < 2) {
        $el.html(Drupal.gridstack.base.imageStyleOptions());

        if (v !== '') {
          $el.val(v).attr('data-imageid', v);
        }
      }
    },

    updateBoxDimensions: function ($box, node) {
      Drupal.gridstack.base.updateBoxDimensions($box, node);
    },

    updateBoxModel: function (box, i, reIndex) {
      var me = this;
      var index = (i + 1);

      box = me.isValidModel(box) ? box : me.collection.at(i);
      reIndex = reIndex || false;

      if (!me.isValidModel(box)) {
        return;
      }

      var id = box.get('id');
      var $box = me.getBoxById(id);
      var node = $box.data('_gridstack_node');
      var data = me.getParsedNode(node);

      if (reIndex) {
        data.index = index;
      }

      if (!_.isEmpty(data)) {
        _.each(data, function (value, key) {
          box.set(key, value, {silent: false});
        });
      }

      if (reIndex) {
        $box.attr('data-gs-index', index);
      }

      me.updateBoxDimensions($box, data);

      // Update nested boxes if required.
      if (me.isNested) {
        me.updateBoxNestedMultiple(box, $box);
      }

      me.collection.trigger('change', box);
    },

    updateBoxModelMultiple: function () {
      var me = this;
      var remove = [];
      var reIndex = false;

      me.collection.each(function (box) {
        if (me.shouldRemove(box)) {
          remove.push(box);
        }
      }, me);

      if (remove.length) {
        reIndex = true;
        me.collection.remove(remove);
      }

      me.collection.each(function (box, i) {
        me.updateBoxModel(box, i, reIndex);
      }, me);

      Drupal.gridstack.base.lastBoxIndex = me.collection.length;
    },

    save: function () {
      var me = this;

      me.updateBoxModelMultiple();

      if (!_.isEmpty(me.saveCallback) && me.isRendered) {
        var o = me.options || {};

        me.saveCallback.callback(me.collection, o);
      }

      me.options.updateIcon = false;
    }

  });

})(jQuery, Drupal, Backbone, _, dBlazy);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc