Sindbad~EG File Manager
/** @namespace H5PEditor */
var H5PEditor = H5PEditor || {};
H5PEditor.VerticalTabs = (function ($) {
/**
* Utility for checking if any of the parent fields is using the VerticalTabs widget
*
* @param {Object} field
* @returns bool
*/
const hasVerticalTabAsParent = function (field) {
if (!field) {
return false;
}
if (field.widget && field.widget instanceof H5PEditor.VerticalTabs) {
return true;
}
if (field.parent) {
return hasVerticalTabAsParent(field.parent);
}
return false;
}
/**
* Draws the list.
*
* @class
* @param {List} list
*/
function VerticalTabs(list) {
var self = this;
if (hasVerticalTabAsParent(list.parent)) {
self.genericList = new H5PEditor.ListEditor(list);
}
else {
var entity = list.getEntity();
// Make first letter upper case.
entity = entity.substr(0,1).toUpperCase() + entity.substr(1);
const innerId = H5P.createUUID();
// Create DOM elements
var $wrapper = $('<div/>', {
'class': 'h5p-vtab-wrapper'
});
var $inner = $('<div/>', {
id: innerId,
'class': 'h5p-vtabs'
}).appendTo($wrapper);
var $container = $('<div/>', {
'class': 'h5p-vtabs-container'
}).appendTo($inner);
var $header = $('<div/>', {
'class': 'h5p-vtabs-header'
}).appendTo($container);
var $tabs = $('<ol/>', {
id: list.getId(),
'aria-describedby': list.getDescriptionId(),
'role': 'tablist',
'class': 'h5p-ul'
}).appendTo($container);
var $createBtn = $('<button />', {
text: H5PEditor.t('core', 'addEntity', {':entity': entity}),
class: 'add-entity'
}).on('click', (e) => {
e.preventDefault();
if (list.addItem()) {
$tabs.children(':last').trigger('open');
toggleOrderButtonsState();
}
}).appendTo($inner);
var $forms = $('<div/>', {
'class': 'h5p-vtab-forms'
}).appendTo($wrapper);
// Once all items have been added we toggle the state of the order buttons
list.once('changeWidget', function () {
toggleOrderButtonsState();
});
$header.html('<span>Collapse</span>');
var $expandCollapseBtn = $('<button />', {
'type': 'button',
'aria-label': 'Collapse',
'aria-expanded': 'true',
'aria-controls': innerId,
'class': 'h5p-vtabs-expand-collapse',
}).appendTo($header);
$expandCollapseBtn.on('click', (e) => {
e.preventDefault();
$inner.toggleClass('is-collapsed');
if ($inner.hasClass('is-collapsed')) {
$expandCollapseBtn.attr('aria-expanded', 'false');
$createBtn.text('');
$createBtn.attr('aria-label', H5PEditor.t('core', 'addEntity', {':entity': entity}));
} else {
$expandCollapseBtn.attr('aria-expanded', 'true');
$createBtn.text(H5PEditor.t('core', 'addEntity', {':entity': entity}));
$createBtn.removeAttr('aria-label');
}
});
}
// Used when dragging items around
var adjustX, adjustY, marginTop, formOffset, $currentTab;
/**
* @private
* @param {jQuery} $item
* @param {jQuery} $placeholder
* @param {Number} x
* @param {Number} y
*/
var moveItem = function ($item, $placeholder, x, y) {
var currentIndex;
// Adjust so the mouse is placed on top of the icon.
x = x - adjustX;
y = y - adjustY;
$item.css({
top: y - marginTop - formOffset.top,
left: x - formOffset.left
});
// Try to move up.
var $prev = $item.prev().prev();
if ($prev.length && y < $prev.offset().top + ($prev.height() / 2)) {
$prev.insertAfter($item);
currentIndex = $item.index();
list.moveItem(currentIndex, currentIndex - 1);
return;
}
// Try to move down.
var $next = $item.next();
if ($next.length && y + $item.height() > $next.offset().top + ($next.height() / 2)) {
$next.insertBefore($placeholder);
currentIndex = $item.index() - 2;
list.moveItem(currentIndex, currentIndex + 1);
}
};
/**
* Re-index labels. Necessary after tabs are sorted or removed.
*
* @private
*/
var reindexLabels = function () {
$tabs.find('.h5p-index-label').each(function (index, element) {
$(element).text(index + 1);
});
toggleOrderButtonsState();
};
/**
* Decode HTML entities
* @param {String}
* @return {String}
*/
var decodeEntity = function(inputStr) {
var textarea = document.createElement("textarea");
textarea.innerHTML = inputStr;
return textarea.value;
}
/**
* Always run after reordering, adding or removing to ensure correct
* state of the order buttons.
*
* @private
*/
var toggleOrderButtonsState = function () {
$tabs.children().each(function (index, element) {
var $tab = $(element);
var isTopTab = !$tab.prev().length;
$tab.find('.order-up').attr('aria-disabled', isTopTab).attr('tabindex', isTopTab ? '-1' : '0');
var isBottomTab = !$tab.next().length;
$tab.find('.order-down').attr('aria-disabled', isBottomTab).attr('tabindex', isBottomTab ? '-1' : '0');
});
};
/**
* Opens the given tab.
*
* @private
* @param {jQuery} $newTab
*/
var openTab = function ($newTab) {
if ($currentTab !== undefined) {
H5PEditor.Html.removeWysiwyg();
$currentTab.removeClass('h5p-current');
}
$newTab.addClass('h5p-current');
$currentTab = $newTab;
};
/**
* Adds UI items to the widget.
*
* @public
* @param {Object} item
*/
self.addItem = function (item) {
if (self.genericList) {
self.genericList.addItem(item);
return;
}
var $placeholder;
var $tab = $('<li/>', {
'class': 'h5p-vtab-li'
}).appendTo($tabs);
/**
* Mouse move callback
*
* @private
* @param {Object} event
*/
var move = function (event) {
moveItem($tab, $placeholder, event.pageX, event.pageY);
};
/**
* Mouse button release callback
*
* @private
*/
var up = function () {
H5P.$window
.unbind('mousemove', move)
.unbind('mouseup', up);
H5P.$body
.attr('unselectable', 'off')
.css({
'-moz-user-select': '',
'-webkit-user-select': '',
'user-select': '',
'-ms-user-select': '',
'overflow': '',
})[0].onselectstart = H5P.$body[0].ondragstart = null;
$tab.removeClass('h5p-moving').css({
width: 'auto',
height: 'auto',
top: '',
left: ''
});
$placeholder.remove();
reindexLabels();
};
/**
* Mouse button down callback
*
* @private
*/
var down = function (event) {
if (event.which !== 1) {
return; // Only allow left mouse button
}
H5P.$window
.mousemove(move)
.mouseup(up);
// Start tracking mouse
H5P.$body
.attr('unselectable', 'on')
.css({
'-moz-user-select': 'none',
'-webkit-user-select': 'none',
'user-select': 'none',
'-ms-user-select': 'none',
'overflow': 'hidden'
})[0].onselectstart = H5P.$body[0].ondragstart = function () {
return false;
};
var offset = $tab.offset();
adjustX = event.pageX - offset.left;
adjustY = event.pageY - offset.top;
marginTop = parseInt($tab.css('marginTop'));
formOffset = $tabs.offsetParent().offset();
// TODO: Couldn't formOffset and margin be added?
var width = $tab.width();
var height = $tab.height();
$tab.addClass('h5p-moving').css({
width: width,
height: height
});
$placeholder = $('<li/>', {
'class': 'h5p-placeholder'
}).insertBefore($tab);
$('<div/>', {
class: 'h5p-vtab-a',
}).appendTo($placeholder);
move(event);
};
/**
* Order current list item up
*
* @private
*/
var moveItemUp = function () {
var $prev = $tab.prev();
if (!$prev.length) {
return; // Cannot move item further up
}
var currentIndex = $tab.index();
$prev.insertAfter($tab);
list.moveItem(currentIndex, currentIndex - 1);
reindexLabels();
};
/**
* Order current ist item down
*
* @private
*/
var moveItemDown = function () {
var $next = $tab.next();
if (!$next.length) {
return; // Cannot move item further down
}
var currentIndex = $tab.index();
$next.insertBefore($tab);
list.moveItem(currentIndex, currentIndex + 1);
reindexLabels();
};
// Handle opening of the tab
$tab.on('open', function () {
openTab($tab.add($form));
});
var mouseDownPos;
// Add clickable label
$('<div/>', {
'class' : 'h5p-vtab-a',
html: '<span class="h5p-index-label">' + ($tab.index() + 1) + '</span><span>.</span> <span class="h5p-label" title="' + entity + '">' + entity + '</span>',
role: 'tab',
tabIndex: 0,
on: {
mouseup: function (e) {
if (!mouseDownPos) {
return;
}
// Determine movement
var xDiff = Math.abs(mouseDownPos.x - e.pageX);
var yDiff = Math.abs(mouseDownPos.y - e.pageY);
var moveThreshold = 20;
// Open tab if moved less than threshold
if (xDiff < moveThreshold && yDiff < moveThreshold) {
$tab.trigger('open');
}
},
mousedown: function (e) {
// Start position
mouseDownPos = {
x: e.pageX,
y: e.pageY
};
// Order element
down(e);
},
keypress: function (e) {
if (e.which === 32) {
e.preventDefault();
$tab.trigger('open');
}
}
}
}).appendTo($tab);
var $tabLabel = $tab.find('.h5p-label');
var setTabLabel = function (label) {
$tabLabel.text(label).attr('title', label);
};
// Add remove button
var $removeWrapper = $('<div/>', {
'class': 'vtab-remove-wrapper',
appendTo: $tab
});
H5PEditor.createButton('remove', H5PEditor.t('core', 'removeItem'), function () {
confirmRemovalDialog.show($(this).offset().top);
}).appendTo($removeWrapper);
// Add buttons for ordering
var $orderWrapper = $('<div/>', {
'class': 'vtab-order-wrapper',
appendTo: $tab
});
H5PEditor.createButton('order-up', H5PEditor.t('core', 'orderItemUp'), moveItemUp).appendTo($orderWrapper);
H5PEditor.createButton('order-down', H5PEditor.t('core', 'orderItemDown'), moveItemDown).appendTo($orderWrapper);
// Create confirmation dialog for removing list item
var confirmRemovalDialog = new H5P.ConfirmationDialog({
dialogText: H5PEditor.t('core', 'confirmRemoval', {':type': entity.toLocaleLowerCase()})
}).appendTo(document.body);
// Remove list item on confirmation
confirmRemovalDialog.on('confirmed', function () {
var $next, index = $tab.index();
if ($tab.hasClass('h5p-current')) {
if (index) {
// Go to previous tab
$next = $tab.prev().add($form.prev());
}
else {
// Go to next tab
$next = $tab.next().add($form.next());
}
if ($next.length) {
// Open another tab
$next.trigger('open');
}
}
list.removeItem(index);
$tab.remove();
$form.remove();
reindexLabels();
});
// Create form wrapper
var $form = $('<fieldset/>', {
'role': 'tabpanel',
'class': 'h5p-vtab-form'
});
if (item instanceof H5PEditor.Group) {
item.on('summary', function (event) {
if (event.data) {
// Update tab with summary
setTabLabel(event.data.substr(0, 32));
}
});
}
// Append new field item to forms wrapper
item.appendTo($form);
// Append form wrapper to forms list
$form.appendTo($forms);
// Good UX: automatically expand groups
if (item instanceof H5PEditor.Group) {
item.expand();
// Remove group title
item.$group.children('.title').remove();
}
else if (item instanceof H5PEditor.Library) {
$form.addClass('content');
let summary = item.$select.children(':selected').text();
if (item.params.metadata && item.params.metadata.title) {
// The given title usually makes more sense than the type name
summary = decodeEntity(item.params.metadata.title) + (!item.libraries || (item.libraries.length > 1 && item.params.metadata.title.indexOf(summary) === -1) ? ' (' + summary + ')' : '');
}
setTabLabel(summary);
// Use selected library as title
item.changes.push(function (library) {
setTabLabel(library ? library.title : '');
});
let lastLib;
const setSummary = function () {
if (item.params && item.params.metadata && item.params.metadata.title) {
// The given title usually makes more sense than the type name
setTabLabel(decodeEntity(item.params.metadata.title) + (item.libraries.length > 1 && item.params.metadata.title.indexOf(lastLib.title) === -1 ? ' (' + lastLib.title + ')' : ''));
}
else {
setTabLabel(lastLib ? lastLib.title : '');
}
};
if (item.metadataForm) {
item.metadataForm.on('titlechange', setSummary);
}
item.change(function (library) {
lastLib = library;
setSummary();
if (item.metadataForm) {
// Update summary when metadata title changes
item.metadataForm.off('titlechange', setSummary);
item.metadataForm.on('titlechange', setSummary);
}
});
}
else if (item instanceof H5PEditor.Select) {
// Use selected value as title
var change = function () {
var value = item.$select.val();
setTabLabel(value === '-' ? entity : item.$select.children('option[value="' + value + '"]').text());
};
item.$select.change(change);
change();
}
if ($currentTab === undefined) {
// Open tab if there are none open
$tab.trigger('open');
}
$tabs.find('.h5p-vtab-li .h5p-vtab-a').first().focus();
};
/**
* Puts this widget at the end of the given container.
*
* @public
* @param {jQuery} $container
*/
self.appendTo = function ($container) {
if (self.genericList) {
self.genericList.appendTo($container);
return;
}
$wrapper.appendTo($container);
};
/**
* Remove this widget from the editor DOM.
*
* @public
*/
self.remove = function () {
if (self.genericList) {
self.genericList.remove();
return;
}
$wrapper.remove();
};
}
return VerticalTabs;
})(H5P.jQuery);
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists