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

js/admin/gridstack.admin.js
/**
 * @file
 * Provides GridStack admin utilities.
 */

(function ($, Drupal, drupalSettings, _, _db, GridStack) {

  'use strict';

  var _xsFixed = false;

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

  /**
   * GridStack form public methods.
   *
   * @namespace
   */
  Drupal.gridstack.form = {
    $form: null,
    $main: null,
    gridStacks: [],
    isNested: false,
    isValid: function (object) {
      return !_.isNull(object) && !_.isUndefined(object);
    },

    hasGridStack: function (el) {
      return el.length ? this.isValid(el[0].gridstack) : this.isValid(el.gridstack);
    },

    /**
     * load a GridStack.
     *
     * @param {HTMLElement} root
     *   The GridStack HTML element.
     * @param {bool} oldData
     *   Whether to load old data such as for a revert.
     *
     * @return {Drupal.gridstack.views.GridStack}
     *   Returns gridstack instance.
     */
    load: function (root, oldData) {
      var me = this;
      var ui = Drupal.gridstack.ui;
      var $root = $(root);
      var data = {
        el: root,
        collection: ui.loadCollection(root, oldData, false),
        isNested: me.isNested,
        options: {
          breakpoint: $root.data('breakpoint'),
          icon: me.$form.data('icon'),
          id: $root.attr('id'),
          isNested: me.isNested || $root.data('framework') === 1,
          storage: $root.data('storage'),
          nestedStorage: $root.data('nestedStorage'),
          noMargin: $('#edit-options-settings-nomargin').prop('checked')
        }
      };

      return ui.loadGridStack(data);
    },

    /**
     * Reverts to the last stored state.
     *
     * This is currently hidden, to avoid complication with multi-breakpoints
     * which had added and removed elements which might be different from the
     * stored data. With multi-breakpoints, this will cause different amounts,
     * which means bad. However it is kept to fix broken XS breakpoint which
     * is still a mistery. Likely due to at least 3 different versions of admin
     * UI with and without XS, such as Bootstrap vs. Foundation vs. JS layouts.
     *
     * @param {HRMLElement} el
     *   The .gridstack--root HTML element.
     */
    revert: function (el) {
      var me = this;
      if (me.hasGridStack(el)) {
        var instance = el[0].gridstack;
        var storage = el.data('storage');
        var nestedStorage = el.data('nestedStorage');
        // For some reason dataset is different from jquery data.
        var grids = el.data('previewGrids') ? JSON.stringify(_db.parse(el[0].dataset.previewGrids)) : '';
        var nested = el.data('nestedGrids') ? JSON.stringify(_db.parse(el[0].dataset.nestedGrids)) : '';

        _.each(me.gridStacks, function (gridStack) {
          if (gridStack.$el.is(el)) {
            instance.removeAll();
            instance.destroy(false);

            el.empty();

            // Load old data.
            me.load(el, true).render();

            $('[data-drupal-selector="' + storage + '"]').val(grids);
            $('[data-drupal-selector="' + nestedStorage + '"]').val(nested);
          }
        });
      }
    }

  };

  /**
   * GridStack form functions.
   *
   * @param {int} i
   *   The index of the current element.
   * @param {HTMLElement} form
   *   The GridStack form HTML element.
   */
  function gridStackForm(i, form) {
    var me = Drupal.gridstack.form;
    var main = '.gridstack--main';
    var framework = '[data-drupal-selector="edit-options-use-framework"]';
    var $framework = $(framework, form);
    var $icon = $('#gridstack-icon');
    var storedIconUrl = $icon.attr('data-url') || '';

    me.$form = $(form);
    me.$main = $(main);
    me.isNested = $framework.prop('checked');
    me.$form.removeClass('is-gs-nojs');

    /**
     * GridStack layout methods applied to the top level GridStack element.
     *
     * @param {int} i
     *   The index of the current element.
     * @param {HTMLElement} root
     *   The GridStack HTML element.
     */
    function gridStackRoot(i, root) {
      var $root = $(root);
      var gridStack = me.load(root, true);

      /**
       * Reacts on button click events.
       *
       * @param {jQuery.Event} e
       *   The event triggered by a `click` event.
       */
      function onSaveIcon(e) {
        e.preventDefault();

        if (e.currentTarget === this) {
          var $btn = $(this);
          var message = $btn.data('message');

          if (gridStack.collection && $root.hasClass('gridstack--main')) {
            gridStack.collection.trigger('gridstack:main:' + message);

            Drupal.gridstack.icon.build(form, true);
          }
        }
      }

      // Render the Backbone GridStack instance.
      gridStack.render();

      // Collect Backbone GridStack instances for aggregated CRUD.
      me.gridStacks.push(gridStack);

      // @todo remove temp fix for broken CSS breakpoint at non-js layouts.
      if (!_xsFixed && me.isNested && $root.hasClass('gridstack--xs')) {
        me.revert($root);
        _xsFixed = true;
      }

      if ($root.hasClass('gridstack--main')) {
        me.$form.off('click.gs.save').on('click.gs.save', '.btn--main.btn--save', onSaveIcon);
      }
    }

    /**
     * Build column selector.
     */
    function onSelectColumn() {
      $(this).change(function (e) {
        if (e.target === this) {
          var $select = $(this);
          var $target = $($select.data('target'));

          if (me.hasGridStack($target)) {
            var instance = $target[0].gridstack;
            var column = $select.val() || $target.data('gsColumn');

            $target.removeClass(function (index, css) {
              return (css.match(/grid-stack-(\d+)/g) || []).join(' ');
            });

            $target.addClass('grid-stack-' + column).attr('data-gs-column', column);

            instance.column(column);
          }
        }
      });
    }

    /**
     * Build width selector which affects GridStack width for correct preview.
     *
     * @param {int} i
     *   The index of the current element.
     * @param {HTMLElement} el
     *   The width input HTML element.
     */
    function onInputWidth(i, el) {
      var $el = $(el);

      var updateWidth = function (input) {
        var gs = input.data('target');
        var $target = $(gs);
        var $subPreview = $target.closest('.gridstack-preview--sub');

        if ($target.length) {
          var value = input.val();
          var w = value ? parseInt(value) : parseInt($target.data('responsiveWidth'));
          var width = w < 600 ? 600 : w;

          if (value !== '') {
            $target.css({width: width});
          }

          $subPreview[value === '' ? 'addClass' : 'removeClass']('form-disabled');
        }
      };

      updateWidth($el);

      $el.on('blur', function (e) {
        if (e.target === this) {
          var input = $(this);
          updateWidth(input);
        }
      });
    }

    /**
     * Sets the framework environment.
     */
    function setFramework() {
      me.isNested = $framework.prop('checked');
      me.$form[me.isNested ? 'addClass' : 'removeClass']('is-framework');
    }

    /**
     * Reacts on form submission.
     *
     * @param {jQuery.Event} e
     *   The event triggered by a `click` event.
     */
    function onFormSubmit(e) {
      me.$form.addClass('is-gs-saving');

      // Some stored values dependent on :visible pseudo will not store with
      // CSS display none, hence force them visible.
      // Claro has class is-selected, Seven selected.
      // Claro has vertical-tabs__item, Seven vertical-tabs__pane.
      // @todo figure out to not rely on themes to avoid incompatibility.
      $('.vertical-tabs > ul > li', me.$form).removeClass('selected is-selected');
      $('.vertical-tabs > ul > li:last a', me.$form).click();
      // @todo remove .vertical-tabs__item, .vertical-tabs__item .details-wrapper, .vertical-tabs__pane
      $('.vertical-tabs > div > details, .vertical-tabs > div > details > div', me.$form).css('display', 'block').addClass('visually-hidden');
      // @todo remove .vertical-tabs__item:last, .vertical-tabs__item:last .details-wrapper, .vertical-tabs__pane:last
      $('.vertical-tabs > div > details:last, .vertical-tabs > div > details:last > div', me.$form).removeClass('visually-hidden');

      // Failsafe to generate icon if "Save & continue" button is not hit.
      $('.btn--gridstack[data-message="save"]').each(function () {
        $(this).trigger('click');
      });
    }

    /**
     * Reacts on button click events.
     *
     * @param {jQuery.Event} e
     *   The event triggered by a `click` event.
     *
     * @todo move it to backbone if doable.
     */
    function onBoxMultiple(e) {
      e.preventDefault();
      e.stopPropagation();

      if (e.currentTarget === this) {
        var btn = this;
        var message = btn.dataset.message;
        var type = btn.dataset.type || 'root';
        var boxId = btn.dataset.gsBid;

        _.each(me.gridStacks, function (gridStack) {

          // Do not use direct child selector (>) to respect nested boxes.
          // Do not use closest() to apply to all breakpoint boxes.
          // Nested boxes are not backbone models, want a backbone model here,
          // meaning must reference its parent box to get the model.
          // The $box must be the actual referenced HTML box regardless models.
          // No need to check for box validity here.
          var $box = $('.box[data-gs-bid="' + boxId + '"]', gridStack.el);
          var box = gridStack.getModel($box);

          gridStack.collection.trigger('gridstack:' + type + ':' + message, e, box, $box);
        });
      }
    }

    /**
     * Adds a new box to all breakpoints once on clicking `Add grid` button.
     *
     * @param {jQuery.Event} e
     *   The event triggered by a `click` event.
     */
    function onAddMultiple(e) {
      e.preventDefault();

      if (e.currentTarget === this) {
        Drupal.gridstack.base.lastBoxIndex++;

        var box = new Drupal.gridstack.models.Box({
          index: Drupal.gridstack.base.lastBoxIndex,
          width: me.isNested ? 12 : 2
        });

        _.each(me.gridStacks, function (gridStack) {
          gridStack.collection.add([box]);
        });
      }
    }

    /**
     * Reacts on button `Revert` click events.
     *
     * @param {jQuery.Event} e
     *   The event triggered by a `click` event.
     */
    function onRevert(e) {
      e.preventDefault();

      if (e.currentTarget === this && this.dataset.target) {
        me.revert($(this.dataset.target));
      }
    }

    // Loop through each GridStack root instance, not nested one.
    $('.gridstack--root', form).each(gridStackRoot);

    // Check if using CSS framework, or GridStack JS.
    setFramework();

    // Display icon if exists at public, or MODULE_NAME/images, directory.
    if (storedIconUrl) {
      var date = new Date();
      $('#gridstack-screenshot', form).html('<img src="' + storedIconUrl + '?rand=' + date.getTime() + '" alt="Icon" />');
    }

    // Run actions.
    me.$form.find('.form-text--width').each(onInputWidth);
    me.$form.find('.form-select--column').each(onSelectColumn);
    me.$form.off('click.gs.framework').on('click.gs.framework', framework, setFramework);
    me.$form.off('click.gs.box').on('click.gs.box', '.btn--box', onBoxMultiple);
    me.$form.off('click.gs.add').on('click.gs.add', '.btn--main.btn--add', onAddMultiple);
    me.$form.off('click.gs.revert').on('click.gs.revert', '.btn--revert', onRevert);

    me.$form.on('submit', onFormSubmit);
  }

  /**
   * Attaches gridstack behavior to HTML element .form--gridstack.
   *
   * @type {Drupal~behavior}
   */
  Drupal.behaviors.gridStackAdmin = {
    attach: function (context) {
      $('.form--gridstack', context).once('form-gridstack').each(gridStackForm);
    }
  };

})(jQuery, Drupal, drupalSettings, _, dBlazy, GridStack);

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

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