slick_browser-8.x-2.1/js/src/slick-browser.widget.js
js/src/slick-browser.widget.js
/**
* @file
* Provides Slick Browser widget utility functions.
*/
(function ($, Drupal, Sortable, _d) {
'use strict';
var _nick = 'sb';
var _isNick = 'is-' + _nick;
var _root = '.' + _nick;
var _idWidget = _nick + '-widget';
var _onWidget = _nick + '--widget--on';
var _baseWidget = _root + '--widget';
var _selWidget = _root + '--widget:not(.' + _onWidget + ')';
var _idSortable = _nick + '-sortable';
var _onSortable = _nick + '__sortable--on';
var _selSortable = _root + '__sortable:not(.' + _onSortable + ')';
var _dataSlickIndex = 'data-slick-index';
var _slickInitialized = 'slick-initialized';
var _selCloned = '.slick-cloned';
var _isActive = _isNick + '-active';
var _isFocused = _isNick + '-focused';
var _selSubmit = '.js-form-managed-file .js-form-submit:not(.button--sb)';
var _cAjax = 'sbAjax';
Drupal.slickBrowser = Drupal.slickBrowser || {};
/**
* Slick Browser widget utility functions.
*
* @param {HTMLElement} widget
* The Slick Browser widget HTML element.
*/
function fbWidget(widget) {
var $widget = $(widget);
var $form = $widget.closest('form');
var widgetId = $widget.attr('id');
var $slider = $('.slick__slider', widget);
var $sliderMain = $('.slick--browser .slick__slider', widget);
var $sbAction = $('.sb__header', widget);
var $dots = $('.slick-dots', widget);
var $grids = $('.blazy--grid', widget);
var $blazy = $('.blazy', widget);
var end = $widget.data('end') ? $widget.data('end') : 0;
var initialized = $sliderMain.hasClass(_slickInitialized);
// We are allowed by Display style to use configurable Blazy Grid instead.
// We use JS since this class is added by theme level later, not module.
$('.media-library-item--grid', widget).removeClass('media-library-item--grid');
/**
* Clean up cloned ids.
*
* @name cleanUp
*/
function cleanUp() {
if (initialized) {
$slider.find(_selCloned).removeAttr('data-entity-id data-row-id data-drupal-selector');
$slider.on('setPosition', function () {
$slider.find(_selCloned).removeAttr('data-entity-id data-row-id data-drupal-selector');
});
}
}
/**
* Update widget height if dots too high.
*
* @name updateWidgetHeight
*/
function updateWidgetHeight() {
if ($dots.length) {
var dh = $dots.height();
var wh = $widget.height() - 80;
if (wh <= dh) {
$widget.css('minHeight', dh + 80);
}
}
if ($grids.length) {
if ($blazy.length) {
Drupal.attachBehaviors($blazy[0]);
}
if ($sliderMain.length) {
$sliderMain[0].slick.refresh();
}
}
}
/**
* Update the view of the working widget.
*
* @name onUpdateView
*
* @param {jQuery.Event} event
* The event triggered, most likely a `click` event.
*/
function onUpdateView(event) {
event.preventDefault();
var $btn = $(event.currentTarget);
var handled = $btn.data('handled');
var target = $btn.data('target');
var $targetId = $widget.siblings('input[type*=hidden][name*="[target_id]"]');
if (target !== 'done') {
$widget.data('deltas', '');
}
$widget.toggleClass('is-sb-' + target);
$.each(['caption', 'crop', 'sort', 'done'], function (i, btn) {
if (target === 'done' || target !== btn) {
$widget.removeClass('is-sb-' + btn);
}
});
$('.button--sb').removeClass(_isActive);
$('.button--' + target, widget)[$widget.hasClass('is-sb-' + target) ? 'addClass' : 'removeClass'](_isActive);
var updateCount = function () {
var count = $widget.find('input[data-entity-id]').length;
$widget.attr('data-sb-count-items', count);
};
updateCount();
switch (target) {
case 'crop':
if (!handled && $sliderMain.length) {
$sliderMain[0].slick.refresh();
}
break;
case 'remove':
var entityId = $btn.data('entityId') || $btn.closest('[data-entity-id]').data('entityId');
var ids = $targetId.val();
var existing = $widget.data('removedId') || '';
$targetId.val($.trim(ids.replace(entityId, '')));
$widget.data('removedId', existing + ' ' + entityId);
rebuildSlick($btn);
$widget.find('.sb__sortable [data-entity-id="' + entityId + '"]').remove();
updateCount();
break;
case 'removeall':
$targetId.val('');
$widget.contents(':not(div[id*="ajax-wrapper"])').remove();
$widget.css('minHeight', 0);
rebuildSlick($btn);
updateCount();
break;
case 'done':
if ($widget.data('deltas')) {
rebuildSlick();
}
if ($sliderMain.length) {
if ($('.slick-prev:not(.slick-disabled)', $sliderMain).length) {
$('.slick-prev:not(.slick-disabled)', $sliderMain).trigger('click');
}
else if ($('.slick-next:not(.slick-disabled)', $sliderMain).length) {
$('.slick-next:not(.slick-disabled)', $sliderMain).trigger('click');
}
else {
$sliderMain[0].slick.refresh();
}
}
updateCount();
break;
default:
break;
}
$btn.data('handled', !handled);
Drupal.slickBrowser.jump(widgetId);
cleanUp();
updateWidgetHeight();
}
/**
* Rebuild the slick instances.
*
* @name rebuildSlick
*
* @param {jQuery.Object} $btn
* The triggering button HTML element.
*/
function rebuildSlick($btn) {
if (!$('.slick', widget).length) {
return;
}
$('.slick', widget).each(function () {
var $slider = $('.slick__slider', this);
var $slide = $('.slide', $slider);
var slick = $slider.slick('getSlick');
var i = 0;
var rebuild = false;
if ($btn) {
if ($btn.data('target') === 'removeall') {
$slider.slick('removeSlide', null, null, true);
// $widget.contents(':not(div[id*="ajax-wrapper"])').remove();
rebuild = true;
}
else {
var index = $btn.closest('.slide').data('slickIndex');
// No need for $slider.slick('refresh');.
$slider.slick('slickRemove', index);
$slide.each(function () {
$(this).attr(_dataSlickIndex, i);
i++;
});
if ($slide.length === 1) {
$widget.empty();
rebuild = true;
}
}
}
else {
$slide.sort(function (a, b) {
return $(a).data('rowId') - $(b).data('rowId');
});
$slider.empty();
$slide.clone().detach().appendTo($slider);
$slider.slick(slick.options);
rebuild = true;
}
if (rebuild) {
Drupal.attachBehaviors($widget[0]);
if ($('.media--player', $slide).length) {
Drupal.attachBehaviors($('.media--player', $slide)[0]);
}
}
});
}
/**
* Fixes for Focal Point indicator conflict with draggable.
*
* @name focalPoint
*/
function focalPoint() {
$('*[draggable!=true]', $sliderMain).unbind('dragstart');
$sliderMain.on('draggable mouseenter mousedown', '.focal-point-indicator', function (e) {
e.stopPropagation();
});
}
/**
* Reveal alt and title for just in case required, but left empty.
*
* @name onRevealText
*
* @param {jQuery.Event} event
* The event triggered, most likely a `click` event.
*/
function onRevealText(event) {
var form = $form.length ? $form[0] : event.delegateTarget;
var sbId = $('.is-sb-error', form).attr('id');
var $slider = $('.sb .slick__slider', form);
var $text = $('.form-text.required', form);
if (!$text.length) {
return;
}
// Prevents false negative form validation with hidden cloned.
$('.slick-cloned .form-text', $slider).removeAttr('required aria-required');
$text.each(function () {
var $that = $(this);
$that.removeClass('error').removeAttr('tabindex');
if (!this.value) {
var emptyIndex = parseInt($that.closest('.slide').data('slickIndex'), 0);
$that.addClass('error');
$that.closest(_baseWidget).addClass('is-sb-error is-sb-caption');
if ($slider.length) {
$slider.slick('slickGoTo', emptyIndex, true);
$slider.slick('slickPause');
}
}
});
Drupal.slickBrowser.jump(sbId);
}
/**
* Reacts on Alt/ Title field clicks.
*
* @name onTextClick
*
* @param {jQuery.Event} e
* The event triggered, a `click` event.
*/
function onTextClick(e) {
$(e.target).parent().addClass(_isFocused);
}
/**
* Reacts on Alt/ Title field blur event.
*
* @name onTextBlur
*
* @param {jQuery.Event} e
* The event triggered, a `click` event.
*/
function onTextBlur(e) {
$(e.target).parent().removeClass(_isFocused);
}
cleanUp();
if ($widget.hasClass('is-sb-1')) {
$('.slick__slide', widget).addClass('slick-current slick-active');
}
if (initialized && !$sbAction.find('.slick__arrow').length) {
$sliderMain.siblings('.slick__arrow').appendTo($sbAction);
}
if (end > 1 && $('.focal-point-indicator', $sliderMain).length) {
focalPoint();
}
// @todo var removedIds = $widget.data('removedId');
// @todo if (removedIds) {
// @todo var ids = removedIds.split(' ');
// @todo $.each(ids, function (i, v) {
// @todo });
// @todo }
$widget.data('deltas', '');
var cSb = 'click.btnSb';
var aSb = 'click.altSb';
var baSb = 'blur.altSb';
$widget.off(cSb).on(cSb, '.button--sb', onUpdateView);
$widget.off(aSb).on(aSb, '.js-form-type-textfield input', onTextClick);
$widget.off(baSb).on(baSb, '.js-form-type-textfield input', onTextBlur);
onRevealText();
$form.on('click.btnDo', '#edit-submit', onRevealText);
updateWidgetHeight();
$widget.addClass(_onWidget);
}
/**
* Slick Browser sortable utility functions.
*
* @param {HTMLElement} elm
* The sortable container HTML element.
*/
function fnSortable(elm) {
var $elm = $(elm);
/**
* Sort the elements.
*
* @name sortItems
*
* @param {jQuery.Event} event
* The event triggered by a `sortable` event.
*/
function sortItems(event) {
var $target = $(event.item);
var $items = $('.sb__sortitem', elm);
var $widget = $target.closest(_baseWidget);
var eb = $target.closest('.is-sb-eb').length;
var ids = [];
var deltas = [];
var delta = 0;
var item;
var len = $items.length;
$('.slick', $widget).each(function () {
var $slider = $('.slick__slider', this);
if ($slider.hasClass(_slickInitialized)) {
$slider.slick('unslick');
}
});
// Cleans up unclean unslick.
$widget.find('.slick__slide.slick-cloned').remove();
// Update the slick slides to match the new ordered elements.
for (var i = 0; i < len; i++) {
item = $items[i];
delta = $(item).data('rowId');
deltas[i] = delta;
$('.sb__weight', item).val(i);
$('.sb__weight option', item).removeAttr('selected');
$('.sb__weight option[value="' + i + '"]', item).prop('selected', true).siblings('option').prop('selected', false);
$widget.find('.slick__slide.slide--' + delta).attr(_dataSlickIndex, i).attr('data-row-id', i);
$(item).attr('data-row-id', i);
if (eb) {
ids[i] = $(item).attr('data-entity-id');
}
}
$widget.data('deltas', deltas);
// Update entity browser target_id fields.
if (eb) {
$widget.siblings('input[type*=hidden][name*="[target_id]"]').val(ids.join(' '));
}
}
Sortable.create(elm, {
draggable: '.sb__sortitem',
// @todo filter: 'a, input, .button, .sb__action',
// @todo handle: '.sb__preview',
onEnd: sortItems
});
$elm.addClass(_onSortable);
}
/**
* Attaches slick browser widget behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.slickBrowserWidget = {
attach: function (context) {
var me = Drupal.slickBrowser;
_d.once(fbWidget, _idWidget, _selWidget, context);
_d.once(fnSortable, _idSortable, _selSortable, context);
$(_selSubmit, context).on('mousedown.' + _cAjax, me.loading);
},
detach: function (context, setting, trigger) {
if (trigger === 'unload') {
$(_selSubmit, context).off('.' + _cAjax);
_d.once.removeSafely(_idWidget, _selWidget, context);
_d.once.removeSafely(_idSortable, _selSortable, context);
Drupal.slickBrowser.loaded();
}
}
};
})(jQuery, Drupal, Sortable, dBlazy);
