io-8.x-1.x-dev/modules/io_browser/js/src/io-browser.form.js
modules/io_browser/js/src/io-browser.form.js
/**
* @file
* Provides IO Browser utilitiy functions.
*/
(function ($, Drupal, _d, _win) {
'use strict';
var _nick = 'ib';
var _ebs = 'entity_browser_select';
var _idForm = _nick + '-form';
var _idDz = _nick + '-dz';
var _idUpload = _nick + '-upload';
var _isNick = 'is-' + _nick;
var _onForm = 'form--ib--on';
var _formHasSelection = 'form--ib-has-selection';
var _selForm = '.form--ib:not(.' + _onForm + ')';
var _selDz = '#ief-dropzone-upload';
var _selUpload = '.form-managed-file__main';
var _entitiesList = 'entities-list';
var _idEntitiesList = _nick + '-' + _entitiesList;
var _onEntitiesList = _entitiesList + '--on';
var _selEntitiesList = '.' + _entitiesList + ':not(.' + _onEntitiesList + ')';
var _selEditSelected = '#edit-selected';
var _ibCounter = 'ibCounter';
var _isMarked = _isNick + '-marked';
var _isChecked = _isNick + '-checked';
var _selIsMarked = '.' + _isMarked;
var _selIsChecked = '.' + _isChecked;
var _isMarkedChecked = _isMarked + ' ' + _isChecked;
var _isEmpty = _isNick + '-empty';
var _isCollapsed = _isNick + '-collapsed';
var _wasChecked = 'was-ib-checked';
var _checked = 'checked';
var _disabled = 'disabled';
var _cItemContainer = 'item-container';
var _selItemContainer = '.' + _cItemContainer;
var _cardinality = 'cardinality';
var _overlimit = 'form--overlimit';
var _dataEntity = 'data-entity';
var _dataEntityId = _dataEntity + '-id';
var _img = 'img';
var _selMessages = '.messages';
var _selBtnShow = '.button-wrap--show-selection';
var _selVFSelection = '.views-field--selection';
var _selGrid = '.grid';
var _btnSelect = '.button--select';
// var _messageTimer;
Drupal.ioBrowser = Drupal.ioBrowser || {};
/**
* IO Browser utility functions.
*
* @namespace
*/
Drupal.ioBrowser.form = Drupal.ioBrowser.form || {
$form: null,
/**
* Checks if selection is empty.
*
* @return {bool}
* True if a selection is not available, else false.
*/
isEmpty: function () {
return !$(_selEditSelected, this.$form).children().length;
},
/**
* Do something if selection is empty.
*/
doEmpty: function () {
// @todo $(_selBtnShow, $footer)[$form.hasClass('form--tabs-v') ? 'show' : 'hide']();
this.$form.addClass(_isEmpty + ' ' + _isCollapsed);
},
/**
* Remove empty marker whenever an item is selected, or uploaded.
*/
noLongerEmpty: function () {
this.$form.removeClass(_isEmpty);
},
/**
* Remove empty marker whenever an item is selected, or uploaded.
*/
onUpload: function () {
this.noLongerEmpty();
},
/**
* Toggle selection counter.
*/
toggleCounter: function () {
var me = this;
var cardinality = me.$form.data(_cardinality) || -1;
var $editSelected = $(_selEditSelected, me.$form);
var $counter = $('#edit-counter', me.$form);
// Only multistep display has selection, so do nothing.
if ($editSelected.length) {
var _selectedCount = $editSelected.children().length;
var total = cardinality === -1 ? 'unlimited' : cardinality;
var text = Drupal.formatPlural(_selectedCount, '1', '@count');
text += ' ' + Drupal.formatPlural(total, 'of 1 item selected', 'of @count items selected');
if (me.$form.hasClass(_overlimit)) {
text += ' ' + Drupal.t('(Remove one to select another)');
$counter.text(_selectedCount > 0 ? text : '');
}
else {
$counter.text(_selectedCount > 0 ? text : '');
}
if (me.isEmpty()) {
me.doEmpty();
}
}
},
/**
* Marks selected item enabled or disabled.
*
* @param {string} value
* The checkbox value.
* @param {boolean} enabled
* Whether to enable or disable.
*/
toggleSelected: function (value, enabled) {
var $input = $('input[name="' + _ebs + '[' + value + ']"]');
var txt = enabled ? '' : Drupal.t('Was selected');
var $grid;
if ($input.length) {
$grid = $input.closest(_selGrid);
if (enabled) {
$input.prop(_checked, false).removeAttr(_disabled);
$grid.removeClass(_isMarkedChecked + ' ' + _wasChecked);
}
else {
$input.prop(_checked, true).attr(_disabled, _disabled);
$grid.addClass(_isMarked + ' ' + _wasChecked);
}
if ($grid.find(_img).length) {
$grid.find(_img).attr('title', txt);
}
else {
$grid.attr('title', txt);
}
}
}
};
/**
* IO Browser utility functions.
*
* @param {HTMLElement} form
* The Entity Browser form HTML element.
*/
function fnForm(form) {
var me = Drupal.ioBrowser.form;
var $form = $(form);
var $body = $form.closest('body');
var $wParent = $(_win.parent.document);
var $dialog = $('.ui-dialog:visible', $wParent);
// @todo var $footer = $('#edit-footer', form);
var $checkBox = $('input[name*="' + _ebs + '"]', form);
var $btnUse = $('.button[name="use_selected"]', form);
var txtUse = $btnUse.length ? $btnUse.val() : Drupal.t('Add to Page');
var clonedUse = '#edit-use-selected-clone';
var $editSelected = $(_selEditSelected, form).removeClass('hidden');
var cardinality = $form.data(_cardinality) || -1;
me.$form = $form;
if ($('.ib__radios', form).length) {
$form.addClass('form--media-bundle-selection');
}
/**
* Selects item within Entity Browser iframes.
*
* @param {jQuery.Event} event
* The event triggered by a `click` event.
*
* @return {bool}|{mixed}
* Return false if no context available.
*/
function onAddItem(event) {
event.preventDefault();
var grid = event.currentTarget;
checkItem(grid);
// Only multistep display has selection, so do nothing.
if (!$editSelected.length) {
$form.addClass(_isCollapsed);
return false;
}
else {
$editSelected.trigger('change.' + _ibCounter);
}
// Show the selection button.
$(_selBtnShow, form).show();
// Refresh selection sortable.
// @todo figure out replacement for deprecated $editSelected.sortable('refresh');
$form.removeClass(_isCollapsed);
}
/**
* Clones item into selection display.
*
* @param {HTMLElement} grid
* The grid HTML element.
*/
function cloneItem(grid) {
var $grid = $(grid);
var $input = $('input[name^="' + _ebs + '"]', grid);
var entity = $input.val();
var split = entity.split(':');
var id = split[1];
var $img = $(_img, grid);
var thumb = $('.media', grid).data('thumb');
// @todo proper preview selection.
var $txt = $(_selVFSelection, grid).length ? $(_selVFSelection, grid) : $('.views-field:nth-child(2)', grid);
var $clone = null;
me.noLongerEmpty();
$grid.attr(_dataEntityId, id).attr(_dataEntity, entity);
// If it has thumbnails.
if (thumb) {
$clone = $('<img src="' + thumb + '" alt="' + Drupal.t('Thumbnail') + '">');
}
// If it has images.
else if ($img.length) {
$clone = $img;
}
// If it has no images, and has a special class .views-field--selection.
// @todo fault proof.
else if ($txt.length) {
$clone = $txt;
}
if ($clone === null) {
return;
}
// Only multistep display has selection, so do nothing.
if (!$editSelected.length) {
return;
}
// @todo recheck dups.
var cloned = $clone.clone();
if (!cloned.closest(_selItemContainer).length) {
cloned
.addClass('item-selected')
.detach()
.appendTo($editSelected)
.wrapAll('<div class="' + _cItemContainer + '" ' + _dataEntityId + '="' + id + '" ' + _dataEntity + '="' + entity + '" />');
}
// Adds dummy elements for quick interaction.
var $weight = '<input class="weight" value="" type="hidden" />';
var $remove = '<span class="button-wrap button-wrap--remove"><input value="Remove" class="button button--remove button--remove-js" type="button"></span>';
$(_selItemContainer, $editSelected).each(function (i) {
var t = $(this);
if (!$('.weight', t).length) {
t.append($remove);
t.append($weight);
$('.button--remove', t).attr(_dataEntityId, id).attr(_dataEntity, entity).attr('data-remove-entity', 'items_' + entity).attr('name', 'remove_' + id + '_' + i);
// <input class="weight" data-drupal-selector="edit-selected-items-220-0-weight" type="hidden" name="selected[items_220_0][weight]" value="0">
$('.weight', t).val(i).attr('name', 'selected[items_' + id + '_' + i + '][weight]').attr('data-drupal-selector', 'edit-selected-items-' + id + '-' + i + '-weight');
}
});
// Remove the clone when the input is unchecked.
if (!$input.prop(_checked)) {
$editSelected.find(_selItemContainer + '[' + _dataEntityId + '="' + id + '"]').remove();
}
}
/**
* Check the EB input when the outer element is clicked.
*
* @param {HTMLElement} grid
* The grid HTML element.
*
* @return {bool}
* Return false if not applicable.
*/
function checkItem(grid) {
var $grid = $(grid);
var input = 'input[name^="' + _ebs + '"]';
var $input = $(input, grid);
var entity = $input.val();
var split = entity.split(':');
var id = split[1];
var $view = $grid.closest('.view--ib');
me.noLongerEmpty();
var checkOne = function () {
$input.prop(_checked, !$input.prop(_checked)).attr(_dataEntityId, id).attr(_dataEntity, entity);
$grid[$input.prop(_checked) ? 'addClass' : 'removeClass'](_isMarkedChecked + '');
$(_btnSelect, grid).html($input.prop(_checked) ? '✓' : '+');
};
var uncheckOne = function () {
$input.prop(_checked, false);
$grid.removeClass(_isMarkedChecked);
$(_btnSelect, grid).html('+');
if ($editSelected.length) {
$editSelected.find(_selItemContainer + '[' + _dataEntityId + '="' + id + '"]').remove();
}
};
var resetAll = function () {
$(input).not($grid.find('input')).prop(_checked, false);
$view.find(_selGrid).not(this).removeClass(_isMarkedChecked);
};
var checkAndClone = function () {
checkOne();
cloneItem(grid);
};
if ($view.find('.' + _wasChecked).length) {
$form.addClass(_formHasSelection);
}
else {
$form.removeClass(_formHasSelection);
}
switch (cardinality) {
case 1:
// Do not proceed if one is already stored, until removed.
if ($view.find('.' + _wasChecked).length) {
$form.addClass(_overlimit);
return false;
}
$form.removeClass(_overlimit);
resetAll();
checkAndClone();
// Remove anything else but the new one selected.
if ($editSelected.length) {
$editSelected.find('.item-container:not([' + _dataEntityId + '="' + id + '"])').remove();
}
break;
case -1:
checkAndClone();
break;
default:
var total = $view.find(_selIsMarked).length;
// Only multistep display has selection, so still check it.
if ($editSelected.length && $editSelected.children().length) {
total = $editSelected.children().length;
}
$form[total === cardinality ? 'addClass' : 'removeClass'](_overlimit);
if (total >= cardinality) {
// @todo resetOne, checkOne? Or let the user remove one instead?
if ($grid.hasClass(_isChecked)) {
uncheckOne();
$form.removeClass(_overlimit);
}
else {
$form.addClass(_overlimit);
}
return false;
}
else {
checkAndClone();
}
break;
}
}
/**
* Removes item within Entity Browser selection display.
*
* @param {jQuery.Event} event
* The event triggered by a `click` event.
*/
function onRemoveItem(event) {
event.preventDefault();
var $btn = $(event.currentTarget);
var $item = $btn.closest(_selItemContainer);
var entity = $item.data('entity');
var $input = $form.find('input[name="' + _ebs + '[' + entity + ']"]');
var $marked = $form.find(_selIsMarked + '[' + _dataEntity + '="' + entity + '"]');
// Remove markers from input container.
$marked.removeClass(_isMarkedChecked);
$input.prop(_checked, false).closest(_selIsChecked).removeClass(_isMarkedChecked);
// Remove selection item as well.
$item.remove();
$form.removeClass(_overlimit);
$(_btnSelect, $marked).html('+');
if ($editSelected.length) {
$editSelected.trigger('change.' + _ibCounter);
}
if (me.isEmpty()) {
me.doEmpty();
}
}
/**
* Toggles the selection displays.
*/
function onToggleSelection() {
$form.toggleClass(_isCollapsed);
}
/**
* Dialog actions.
*/
function doDialog() {
var $fake = $('<button id="edit-use-selected-clone" class="button button--primary button--ib button--use-selected-clone">' + txtUse + '</button>');
var $close = $dialog.eq(0).find('.ui-dialog-titlebar-close');
if ($btnUse.length && !$dialog.find(clonedUse).length) {
$fake.insertBefore($close);
}
$dialog.on('click.ibDialogInsert', clonedUse, function (e) {
$(e.delegateTarget).addClass('is-b-loading');
$btnUse.click();
});
}
/**
* Remove annoying messages on small window by clicking it.
*
* @param {Event} e
* The click event.
*/
function onCloseMessages(e) {
$(e.target).remove();
}
/**
* Finalizes the form actions.
*/
function fnFinalize() {
// Remove giant dup messages since we are on small windows, need room.
// _win.clearTimeout(_messageTimer);
// _messageTimer = _win.setTimeout(function () {
// $(_selMessages, $body).remove();
// }, 9000);
$body.addClass('ib-body');
$('> div:not(.ib__aside)', form).addClass('ib__main');
// Only proceed if we have selections.
if (me.isEmpty()) {
me.doEmpty();
if ($dialog.length) {
$dialog.find(clonedUse).remove();
}
return;
}
// Do dialog stuffs.
if ($dialog.length) {
doDialog();
}
$(clonedUse).text(txtUse).removeClass('visually-hidden');
// This selection can be loaded anywhere out of Views, form upload, etc.
if (!$checkBox.length) {
return;
}
me.noLongerEmpty();
}
// Events.
$form.on('click.ibGrid', '.grid:not(.view-list--header, .' + _wasChecked + ')', onAddItem);
$form.on('click.ibRemove', '.button--remove-js', onRemoveItem);
// button--show-selection
$form.on('click.ibShow', '.entity-browser-show-selection', onToggleSelection);
// $form.on('click.ibUpload', '.js-form-file', me.onUpload.bind(me));
$form.on('click.ibInsert', '#edit-use-selected', Drupal.ioBrowser.loading);
$form.on('click.ibSubmit', '#edit-submit', Drupal.ioBrowser.loading);
$body.on('click.ibMessage', _selMessages, onCloseMessages);
fnFinalize();
$form.addClass(_onForm);
}
/**
* IO Browser utility functions.
*
* @param {HTMLElement} elm
* The #edit-selected HTML element.
*/
function fnEntitiesList(elm) {
var me = Drupal.ioBrowser.form;
var $elm = $(elm);
var $form = $elm.closest('form');
var targetType = $form.data('targetType');
me.$form = $form;
$elm.children().each(function (i, item) {
var $item = $(item);
var id = $item.data('entityId');
var $input = $('input', item);
var value = targetType + ':' + id;
// @todo $input.attr(_dataEntity, value);
me.toggleSelected(value, false);
$input.on('mousedown', function () {
me.toggleSelected(value, true);
// Should listen to ajaxing, maybe later.
_win.setTimeout(function () {
$elm.trigger('change.' + _ibCounter);
}, 300);
});
});
var checkCounter = function () {
me.toggleCounter();
};
checkCounter();
$elm.on('change.' + _ibCounter, checkCounter);
$elm.addClass(_onEntitiesList);
}
/**
* IO Browser dropzone functions.
*
* @param {HTMLElement} elm
* The #ief-dropzone-upload HTML element.
*/
function fnDz(elm) {
var me = Drupal.ioBrowser.form;
var $elm = $(elm);
if (!$elm.is(':empty')) {
me.noLongerEmpty();
}
}
/**
* IO Browser dropzone functions.
*
* @param {HTMLElement} elm
* The .form-managed-file__main HTML element.
*/
function fnUpload(elm) {
var me = Drupal.ioBrowser.form;
var $elm = $(elm);
if (!$(_selDz).length) {
if ($elm.siblings('.js-form-item').length) {
me.noLongerEmpty();
}
else {
if ($('#edit-selected').is(':empty')) {
me.doEmpty();
}
}
}
}
/**
* Attaches IO Browser form behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.ioBrowserForm = {
attach: function (context) {
_d.once(fnForm, _idForm, _selForm, context);
_d.once(fnEntitiesList, _idEntitiesList, _selEntitiesList, context);
_d.once(fnDz, _idDz, _selDz, context);
_d.once(fnUpload, _idUpload, _selUpload, context);
},
detach: function (context, setting, trigger) {
if (trigger === 'unload') {
_d.once.removeSafely(_idForm, _selForm, context);
_d.once.removeSafely(_idEntitiesList, _selEntitiesList, context);
_d.once.removeSafely(_idDz, _selDz, context);
_d.once.removeSafely(_idUpload, _selUpload, context);
}
}
};
})(jQuery, Drupal, dBlazy, this);
