/* eslint-disable no-undef */
import {
RcmailDialog,
RcmailDialogButton,
} from '../../../mel_metapage/js/lib/classes/modal.js';
import { MelHtml } from '../../../mel_metapage/js/lib/html/JsHtml/MelHtml.js';
import { MelObject } from '../../../mel_metapage/js/lib/mel_object.js';
import { MelLinkVisualizer, MelFolderLink, MelStoreLink } from './mel_link.js';
import { MelIconPrevisualiser } from '../../../mel_metapage/skins/mel_elastic/js_templates/blocks/icon_previsualiser.js';
export class LinkManager extends MelObject {
constructor({
displayButtonElement = $('.module_Links .melv2-card-pre'),
} = {}) {
super(displayButtonElement);
}
main(displayButtonElement) {
super.main(displayButtonElement);
console.log('link, manager');
if (rcmail.env.mel_portal_ulink) this.displayButton(displayButtonElement);
this.displayLinks();
this.bindActions();
window.linkManager = this;
if (window.linksPicturesLoaded) {
onLoaded();
}
}
/**
* Créé la modale de création/modification d'un nouveau lien
* @param {?string} id
* @param {?string} title
* @param {?string} url
* @param {?string} icon
*/
openLinkModal(id = null, title = null, url = null, icon = null) {
let self = this;
if (this.newLinkModal) {
this.newLinkModal.show();
this.getModalValue(id, title, url, icon);
// this.bindModalActions();
} else {
const html = MelHtml.start
.div()
.row({ class: 'mx-2' })
.span({ class: 'text-danger' })
.text('*')
.end()
.text(rcmail.gettext('required_fields', 'mel_useful_link'))
.end()
.input({ id: 'mulc-id', type: 'hidden', value: id })
.row({ class: 'mx-2' })
.label({ class: 'span-mel t1 first', for: 'mulc-title' })
.span({ class: 'text-danger' })
.text('*')
.end()
.text(rcmail.gettext('link_name', 'mel_useful_link'))
.end()
.input({
id: 'mulc-title',
class: 'form-control input-mel required',
required: true,
placeholder: rcmail.gettext('link_title', 'mel_useful_link'),
value: title,
})
.end()
.row({ class: 'mx-2' })
.label({ class: 'span-mel t1 first', for: 'mulc-url' })
.span({ class: 'text-danger' })
.text('*')
.end()
.text(rcmail.gettext('link_url', 'mel_useful_link'))
.end()
.input({
id: 'mulc-url',
class: 'form-control input-mel required',
required: true,
placeholder: 'URL',
value: url,
})
.end()
.row({ class: 'mr-1 mt-3 mx-2 align-items-center' })
.label({ class: 'span-mel t1 first', for: 'mulc-url' })
.text(rcmail.gettext('preview', 'mel_useful_link'))
.end()
.div({ class: 'link-block' })
.div({ class: 'link-icon-container no-after' })
.img({
id: 'icon-image',
class: `link-icon-image ${icon ? 'hidden' : ''}`,
src: '',
onerror: "imgError(this.id, 'no-image')",
style: 'display:none',
})
.span({
id: 'no-image',
class: `link-icon-no-image ${icon ? 'hidden' : ''}`,
})
.end('span')
.icon(icon ?? '', {
id: 'link-icon',
class: `link-with-icon ${!icon ? 'hidden' : ''}`,
})
.end('icon')
.end('div')
.end('div')
.button({ id: 'change_icon', class: '' })
.text(rcmail.gettext('change_icon', 'mel_useful_link'))
.icon('edit')
.end()
.end('button')
.end('row')
.end('div')
.generate();
this.newLinkModal = new RcmailDialog(html, {
title: id
? rcmail.gettext('update_new_link', 'mel_useful_link')
: rcmail.gettext('create_new_link', 'mel_useful_link'),
buttons: [
new RcmailDialogButton(
id
? rcmail.gettext('update', 'mel_useful_link')
: rcmail.gettext('add', 'mel_useful_link'),
{
id: 'add-mel-link',
classes: 'add-mel-link mel-button btn btn-secondary',
click: () => {
if (self.checkEmptyInputs()) this.addMelLink();
},
},
),
],
options: { disable_show_on_start: true, height: 430 },
});
this.newLinkModal = this.newLinkModal.to_mel_dialog();
this.newLinkModal.show();
if (url && !icon) {
this.displayIcon(url);
}
this.bindModalActions();
}
}
/**
* Créé la modale de modification d'un dossier
* @param {?string} id
* @param {?string} title
*/
openFolderModal(id = null, title = null) {
const html = MelHtml.start
.div()
.input({ id: 'mulc-id', type: 'hidden', value: id })
.row({ class: 'mx-2' })
.label({ class: 'span-mel t1 first', for: 'mulc-title' })
.span({ class: 'text-danger' })
.text('*')
.end()
.text(rcmail.gettext('folder_name', 'mel_useful_link'))
.end()
.input({
id: 'folder-mulc-title',
class: 'form-control input-mel required',
required: true,
placeholder: rcmail.gettext('link_title', 'mel_useful_link'),
value: title,
})
.end()
.end()
.generate();
this.newFolderModal = new RcmailDialog(html, {
title: rcmail.gettext(
rcmail.gettext('update_new_folder', 'mel_useful_link'),
'mel_useful_link',
),
buttons: [
new RcmailDialogButton(rcmail.gettext('update', 'mel_useful_link'), {
id: 'modify-mel-folder',
classes: 'modify-mel-folder mel-button btn btn-secondary',
click: () => {
this.updateFolder(id);
},
}),
],
});
}
/**
* Créé la modale de bibliothèque d'application
*/
openStoreModal() {
const html = MelHtml.start
.div({ class: 'container' })
.row({ class: 'mx-2' })
.div({ class: 'border-bottom-input-icon mb-2' })
.input({
type: 'text',
placeholder: 'Rechercher',
class: 'form-control input-mel border-bottom-input large-input',
id: 'search-app',
oninput: () => {
this.searchInStore($('#search-app').val());
},
})
.button({
id: 'reset-search',
class:
'border-bottom-input-button close-button btn btn-secondary border-0',
})
.removeClass('mel-button')
.icon('close')
.end()
.end('button')
.end('div')
.end('row')
.row({ class: 'm-2' })
.span({ class: 'font-weight-bold' })
.text(rcmail.gettext('category_filter', 'mel_useful_link'))
.end('span')
.end('row')
.row({class: 'm-2 list-filter-container overflow-auto'})
.div({ id: 'list-filters-container' })
.end('div')
.end('row')
.row({ class: 'mx-2' })
.span({
id: 'not-found-app',
class: 'w-100 text-center font-weight-bold',
})
.end()
.ul({ id: 'list-store-app' })
.end('ul')
.end('row')
.end()
.generate();
this.newStoreModal = new RcmailDialog(html, {
title: rcmail.gettext('app_store_title', 'mel_useful_link'),
options: { height: 600, minWidth: 711 },
});
this.loadStoreDialog();
}
/**
* Ajoute un lien de la bibliothèque d'application dans les liens de l'utilisateur
* @param {MelLinkVisualizer} link
*/
addStoreLink(link) {
link.callUpdate().then((data) => {
if (data === link.id) {
this.displayLink(link);
this.loadStoreApp();
}
});
}
/**
* Charge la bibliothèque d'application et ses fonctions
*/
loadStoreDialog() {
$('#reset-search').on('click', () => {
$('#search-app').val('');
this.filterList('all');
});
this.loadStoreApp();
this.activeStoreCategories = [];
this.loadStoreFilters();
}
/**
* Fonction pour la recherche dans le store d'application
* @param {string} input
*/
searchInStore(input) {
let foundLinks = [];
Object.keys(rcmail.env.default_links).forEach((key) => {
if (
rcmail.env.default_links[key].name
.toLowerCase()
.includes(input.toLowerCase())
) {
foundLinks.push(key);
}
});
this.removeFilter();
this.loadStoreApp([], foundLinks);
}
/**
* Charge les applications de la bibliothèque
* @param {Array} filter
* @param {Array} foundLinks
*/
loadStoreApp(filter = [], foundLinks = null) {
$('#list-store-app').empty();
this.storeCategories = [];
let isLinks = false;
for (const item in rcmail.env.default_links) {
if (Object.hasOwnProperty.call(rcmail.env.default_links, item)) {
const link = rcmail.env.default_links[item];
if (foundLinks) {
if (foundLinks.indexOf(item) !== -1) {
let foundLink = new MelStoreLink(
item,
link.name,
link.url,
link.icon,
link.description,
this.linksIdList.includes(item) ? true : false,
link.image ?? null
);
foundLink.displayStoreLink().appendTo('#list-store-app');
isLinks = true;
}
continue;
}
let filterPass = filter.length === 0 ? true : false;
if (link.categories) {
link.categories.forEach((value) => {
if (this.storeCategories.indexOf(value) === -1) {
this.storeCategories.push(value);
}
if (filter.length !== 0) {
filterPass = filter.includes(value) ? true : false;
}
});
}
//!link.feedUr permet de ne pas mettre les anciens liens dans la boucle
if (filterPass && !link.feedUrl) {
let storeLink = new MelStoreLink(
item,
link.name,
link.url,
link.icon,
link.description,
this.linksIdList.includes(item) ? true : false,
link.image ?? null
);
storeLink.displayStoreLink().appendTo('#list-store-app');
isLinks = true;
}
}
}
!isLinks
? $('#not-found-app').text(
rcmail.gettext('not_found_link', 'mel_useful_link'),
)
: $('#not-found-app').text('');
}
/**
* Charge les filtres de la bibliothèque d'application
*/
loadStoreFilters() {
if (this.storeCategories) {
let html = MelHtml.start.ul({ id: 'list-filters' });
html
.li()
.button({
class: 'list-filter active',
id: 'all',
onclick: () => {
this.filterList('all');
},
})
.text('Tout')
.end()
.end('li');
for (const categorie of this.storeCategories) {
html
.li()
.button({
class: 'list-filter',
id: `${categorie}`,
onclick: () => {
this.filterList(categorie);
},
})
.text(categorie)
.end()
.end('li');
}
html.end('ul');
$('#list-filters-container').append(html.generate());
}
}
/**
* Filtre les applications de la bibliothèque d'application
* @param {string} filter
*/
filterList(filter) {
if (filter === 'all') {
this.activeStoreCategories = [];
} else {
const index = this.activeStoreCategories.indexOf(filter);
if (index === -1) {
this.activeStoreCategories.push(filter);
} else {
this.activeStoreCategories.splice(index, 1);
}
}
this.loadStoreApp(this.activeStoreCategories);
this.displayActiveCategories();
}
/**
* Affiche les catégories actives dans le store d'application
*/
displayActiveCategories() {
let self = this;
let active = false;
$('#list-filters li button').each(function () {
if (self.activeStoreCategories.includes($(this).attr('id'))) {
$(this).addClass('active');
active = true;
} else {
$(this).removeClass('active');
}
});
if (!active) {
$('#list-filters li button#all').addClass('active');
}
}
/**
* Supprime les filtres actifs
*/
removeFilter() {
this.activeStoreCategories = [];
this.displayActiveCategories();
}
/**
* Affiche les liens sur la page web
*/
displayLinks() {
let links_array = [];
this.linksIdList = [];
for (const links in rcmail.env.mul_items) {
let link = rcmail.env.mul_items[links];
link = JSON.parse(link);
let linkVisualizer;
if (link.links) {
linkVisualizer = new MelFolderLink(link.id, link.title, link.links);
linkVisualizer.displayFolder().appendTo('.links-items');
for (const key in linkVisualizer.links) {
let subLink = linkVisualizer.links[key];
if (!subLink.icon) {
if (!subLink.image) {
subLink.image = LinkManager.fetchIcon(subLink.link);
}
} else {
subLink.image = '';
}
//Correction problème icon false
if (subLink.icon === 'false') {
subLink.icon = '';
}
linkVisualizer.links[key] = new MelLinkVisualizer(
subLink.id,
subLink.title,
subLink.link,
subLink.image,
true,
subLink.icon,
);
linkVisualizer.links[key]
.displaySubLink()
.appendTo(`#links-container-${linkVisualizer.id}`);
this.linksIdList.push(subLink.id);
}
} else {
if (!link.icon) {
if (!link.image) {
link.image = LinkManager.fetchIcon(link.link);
}
} else {
link.image = '';
}
//Correction problème icon false
if (link.icon === 'false') {
link.icon = '';
}
linkVisualizer = new MelLinkVisualizer(
link.id,
link.title,
link.link,
link.image,
false,
link.icon,
);
linkVisualizer.displayLink().appendTo('.links-items');
this.linksIdList.push(link.id);
}
links_array.push(linkVisualizer);
}
$('<li class="link-space-end"></li>').appendTo('.links-items');
this.bindRightClickActions();
rcmail.env.mul_items = links_array;
}
/**
* Affiche un lien sur la page web
* @param {MelLinkVisualizer} link
*/
displayLink(link) {
link.displayLink().insertBefore('.link-space-end');
this.saveLink(link);
}
/**
* Affiche le bouton de création sur la page web
*/
displayButton(selector) {
let button = MelHtml.start
.div({ class: 'mul_right_buttons' })
.button({
class: 'fixed_mulba',
id: 'mulba',
})
.text('Ajouter')
.icon('add_circle')
.end()
.end('button')
.button({ id: 'app_store', class: 'mel-button-icon' })
.text("Bibliothèque d'applications")
.icon('widgets')
.end()
.end('button')
.end('div');
selector.append(button.generate());
}
/**
* Sauvegarde un lien
* @param {MelLinkVisualizer} link
*/
saveLink(link) {
this.bindRightClickActions(link.id);
this.bindActions(link.id);
rcmail.env.mul_items.push(link);
this.linksIdList.push(link.id);
}
/**
* Affiche un dossier sur la page web
* @param {MelFolderLink} folder
* @param {HTMLElement} location
*/
displayFolder(folder, location = null) {
const indexes = [];
if (!location) {
folder.displayFolder().insertBefore('.link-space-end');
} else {
folder.displayFolder().insertBefore(location);
}
for (const key in folder.links) {
let subLink = folder.links[key];
let index = rcmail.env.mul_items.findIndex(
(item) => item.id === subLink.id,
);
indexes.push(index);
this.removeContainer($('#link-block-' + subLink.id));
subLink.displaySubLink().appendTo(`#links-container-${folder.id}`);
this.bindRightClickActions(subLink.id);
this.bindActions(subLink.id);
}
rcmail.env.mul_items = rcmail.env.mul_items.filter(
(value, index) => !indexes.includes(index),
);
rcmail.env.mul_items.splice(Math.min(...indexes), 0, folder);
this.bindRightClickActions(folder.id);
this.bindActions(folder.id);
}
/**
* Ajoute ou retire un lien d'un dossier
* @param {MelFolderLink} folder
* @param {MelLinkVisualizer} link
*/
updateFolderLink(folder, link) {
folder.addLink(link);
folder.callFolderUpdate().then(() => {
$('#link-block-' + link.id)
.closest('.link-block-container')
.remove();
let index = rcmail.env.mul_items.findIndex((item) => item.id === link.id);
rcmail.env.mul_items.splice(index, 1);
link
.displaySubLink(folder.isOpen)
.appendTo(`#links-container-${folder.id}`);
this.bindRightClickActions(link.id);
this.bindActions(link.id);
});
}
/**
* Ajoute un lien dans la liste des liens de l'utilisateur
*/
addMelLink() {
let linkId = $(LinkManager.SELECTOR_MODAL_ID).val();
let link;
if (!linkId) {
link = new MelLinkVisualizer(
linkId,
$(LinkManager.SELECTOR_MODAL_TITLE).val(),
$(LinkManager.SELECTOR_MODAL_URL).val(),
LinkManager.fetchIcon($(LinkManager.SELECTOR_MODAL_URL).val()),
null,
LinkManager.SELECTEDICON,
);
link.callUpdate().then((data) => {
if (data !== link.id) {
link.id = data;
this.displayLink(link);
}
this.newLinkModal.hide();
});
} else {
for (const key in rcmail.env.mul_items) {
const item = rcmail.env.mul_items[key];
if (item.id === linkId) {
link = item;
let savelink = { ...link };
link.title = $(LinkManager.SELECTOR_MODAL_TITLE).val();
link.link = $(LinkManager.SELECTOR_MODAL_URL).val();
link.image = LinkManager.fetchIcon(
$(LinkManager.SELECTOR_MODAL_URL).val(),
);
link.icon = LinkManager.SELECTEDICON;
link.callUpdate().then((data) => {
this.newLinkModal.hide();
if (!data) {
link.title = savelink.title;
link.link = savelink.link;
link.image = savelink.image;
link.icon = savelink.icon;
}
});
break;
} else if (this.isFolder(item)) {
let findLink = item.getLink(linkId);
if (findLink) {
let savelink = { ...findLink };
findLink.title = $(LinkManager.SELECTOR_MODAL_TITLE).val();
findLink.link = $(LinkManager.SELECTOR_MODAL_URL).val();
findLink.image = LinkManager.fetchIcon(
$(LinkManager.SELECTOR_MODAL_URL).val(),
);
findLink.icon = LinkManager.SELECTEDICON;
item.callFolderUpdate().then((data) => {
this.newLinkModal.hide();
if (!data) {
findLink.title = savelink.title;
findLink.link = savelink.link;
findLink.image = savelink.image;
findLink.icon = savelink.icon;
}
});
break;
}
}
}
}
LinkManager.SELECTEDICON = null;
}
/**
* Retourne un lien par son id
* @param {string} id
* @returns {MelLinkVisualizer | boolean} Return le lien ou false si pas trouvé
*/
findLinkById(id) {
for (const key in rcmail.env.mul_items) {
const item = rcmail.env.mul_items[key];
if (item.id === id) {
return item;
} else if (this.isFolder(item)) {
let findLink = item.getLink(id);
if (findLink) {
return findLink;
}
}
}
return false;
}
/**
* Trouve le dossier parent d'un lien
* @param {MelLinkVisualizer} link
* @returns {MelFolderLink | boolean} Return le dossier ou false si pas trouvé
*/
findParentFolder(link) {
for (const key in rcmail.env.mul_items) {
const item = rcmail.env.mul_items[key];
if (this.isFolder(item)) {
let findLink = item.getLink(link.id);
if (findLink) {
return item;
}
}
}
return false;
}
/**
* Vérifie si un lien est un dossier
* @param {MelLinkVisualizer | MelFolderLink} link
* @returns {Boolean}
*/
isFolder(link) {
if (link.links) return true;
return false;
}
/**
* Vérifie si un lien est dans un dossier
* @param {MelLinkVisualizer | MelFolderLink} link
* @returns {Boolean}
*/
isInFolder(link) {
if (link.inFolder === true) return true;
return false;
}
/**
* Supprime un lien
* @param {string} id
*/
deleteMelLink(id) {
const link = this.findLinkById(id);
if (
confirm(
link.links
? rcmail.gettext('confirm_delete_link_folder', 'mel_useful_link')
: rcmail.gettext('confirm_delete_link_element', 'mel_useful_link'),
)
) {
if (this.isInFolder(link)) {
let folder = this.findParentFolder(link);
folder.removeLink(link);
folder.callFolderUpdate().then(() => {
$('#link-block-' + link.id).remove();
this.linksIdList = this.linksIdList.filter((item) => item !== id);
});
} else if (this.isFolder(link)) {
for (const key in link.links) {
const element = link.links[key];
this.linksIdList = this.linksIdList.filter(
(item) => item !== element.id,
);
}
link.callDelete();
} else {
link.callDelete();
this.linksIdList = this.linksIdList.filter((item) => item !== id);
}
}
}
/**
* Met a jour le titre d'une modale
* @param {string} id
*/
updateFolder(id) {
let folder = rcmail.env.mul_items.find(function (objet) {
return objet.id === id;
});
folder.title = $(LinkManager.FOLDER_SELECTOR_MODAL_TITLE).val();
folder.callFolderUpdate().then(() => {
this.newFolderModal.destroy();
});
}
/**
* Retire un lien d'un dossier pour l'ajouter dans la liste de l'utilisateur
* @param {MelFolderLink} folder
* @param {MelLinkVisualizer} link
* @param {string} id
* @param {int} targetIndex
* @param {?HTMLElement} location
*/
TakeOutLinkFromFolder(folder, link, id, targetIndex, location = null) {
folder.removeLink(link);
//On supprime le dossier s'il n'y a plus aucun lien
if (Object.keys(folder.links).length === 0) {
link.callUpdate().then(() => {
if (!location) {
link.displayLink().insertBefore('.link-space-end');
} else {
link.displayLink().insertBefore(location);
}
this.saveLink(link);
});
folder.callFolderDelete();
this.updateList(id, targetIndex);
} else {
folder.callFolderUpdate().then(() => {
link.callUpdate().then(() => {
$('#link-block-' + link.id).remove();
if (!location) {
link.displayLink().insertBefore('.link-space-end');
} else {
link.displayLink().insertBefore(location);
}
this.saveLink(link);
if (targetIndex !== -1) {
this.updateList(id, targetIndex);
}
});
//Si il ne reste plus qu'un lien dans le dossier, on le sort et supprime le dossier
// if (Object.keys(folder.links).length === 1) {
// this.TakeOutLinkFromFolder(
// folder,
// folder.links[Object.keys(folder.links)[0]],
// );
// }
});
}
}
/**
* Bind des actions liés aux liens
* @param {?string} id on met l'id si on active les actions pour un nouveau lien
*/
bindActions(id = null) {
let self = this;
let _id = id ? `#link-block-${id} ` : '';
if (!id) {
$(LinkManager.CREATE_BUTTON).on('click', function () {
self.openLinkModal();
});
$(LinkManager.APP_STORE).on('click', function () {
self.openStoreModal();
});
}
$(_id + LinkManager.COPY_LINK).on('click', function (e) {
mel_metapage.Functions.copy($(e.currentTarget).attr('data-link'));
});
$(_id + LinkManager.MODIFY_LINK).on('click', function (e) {
self.openLinkModal(
$(e.currentTarget).attr('data-id'),
$(e.currentTarget).attr('data-title'),
$(e.currentTarget).attr('data-link'),
$(e.currentTarget).attr('data-icon'),
);
});
$(_id + LinkManager.DELETE_LINK).on('click', function (e) {
self.deleteMelLink($(e.currentTarget).attr('data-id'));
});
$(_id + LinkManager.MODIFY_FOLDER).on('click', function (e) {
self.openFolderModal(
$(e.currentTarget).attr('data-id'),
$(e.currentTarget).attr('data-title'),
);
});
$(_id + LinkManager.DELETE_FOLDER).on('click', function (e) {
self.deleteMelLink($(e.currentTarget).attr('data-id'));
});
if (!id) {
document.addEventListener('dragenter', function (event) {
if (event.target.classList.contains('link-space-between')) {
event.target.classList.add('link-space-hovered');
}
const linkBlock = event.target.closest(
'.link-block:not(.multilink-open)',
);
if (
linkBlock &&
!linkBlock.parentElement.classList.contains('multilink-container')
) {
linkBlock.classList.add('link-block-hovered');
}
});
document.addEventListener('dragleave', function (event) {
if (event.target.classList.contains('link-space-between')) {
event.target.classList.remove('link-space-hovered');
}
if (event.target.closest('.link-block.link-block-hovered')) {
event.target
.closest('.link-block.link-block-hovered')
.classList.remove('link-block-hovered');
}
});
document.addEventListener(
'dragover',
function (event) {
// Empêche le comportement par défaut afin d'autoriser le drop
event.preventDefault();
},
false,
);
document.addEventListener('drop', function (event) {
event.preventDefault();
let data = JSON.parse(event.dataTransfer.getData('text/plain'));
let id = data.id;
let movedElement = $('#link-block-' + id);
let movedContainer = movedElement.closest('.link-block-container');
let targetElement = $(event.target);
let targetContainer = targetElement.closest('.link-block-container');
let targetIndex = $('.link-block-container').index(targetContainer);
let elementIndex = $('.link-block-container').index(movedContainer);
//Si on déplace un element d'un dossier non ouvert
if (movedElement.hasClass('sublink')) {
targetElement.removeClass('multilink-block-hovered');
targetElement.removeClass('link-space-hovered');
return;
}
//Si on sort un lien d'un dossier
if (data.inFolder) {
let link = self.findLinkById(id);
let folder = self.findParentFolder(link);
//TODO Mettre à jours rcmail.env.mul_items
self.TakeOutLinkFromFolder(
folder,
link,
id,
targetIndex,
targetElement.hasClass('link-space-end') ? null : targetContainer,
);
return;
}
//Si on déplace l'élément
if (targetElement.hasClass('link-space-between')) {
targetElement.removeClass('link-space-hovered');
self.updateList(id, targetIndex, targetContainer, movedContainer);
return;
}
if (targetElement.hasClass('link-space-end')) {
self.updateList(id, targetIndex, targetElement, movedContainer);
return;
}
//Si on le rajoute dans un dossier
if (
targetElement.hasClass('multilink-icon-container') ||
targetElement.hasClass('sublink') ||
targetElement.hasClass('multilink-container')
) {
targetElement.removeClass('multilink-block-hovered');
if (!rcmail.env.mul_items[elementIndex].links) {
self.updateFolderLink(
rcmail.env.mul_items[targetIndex],
rcmail.env.mul_items[elementIndex],
);
}
}
//Si on crée un dossier
else {
targetElement
.closest('.link-block.link-block-hovered')
.removeClass('link-block-hovered');
//Si le target n'est pas déjà un dossier
if (!rcmail.env.mul_items[targetIndex].links) {
//Si on déplace un dossier dans un dossier
if (rcmail.env.mul_items[elementIndex].links) return;
let _melFolder = new MelFolderLink('', 'Dossier', [
rcmail.env.mul_items[elementIndex],
rcmail.env.mul_items[targetIndex],
]);
_melFolder.callFolderUpdate().then((data) => {
if (data !== _melFolder.id) {
_melFolder.id = data;
self.displayFolder(_melFolder, targetContainer);
}
});
}
//Si on ajoute un lien dans un dossier
else {
targetElement.removeClass('multilink-block-hovered');
if (!rcmail.env.mul_items[elementIndex].links) {
self.updateFolderLink(
rcmail.env.mul_items[targetIndex],
rcmail.env.mul_items[elementIndex],
);
}
}
}
});
}
}
/**
* Met a jour l'ordre des liens
* @param {string} id
* @param {int} newIndex Nouvelle position de l'icone dans le DOM
*/
updateList(id, newIndex, targetContainer = null, movedContainer = null) {
const busy = rcmail.set_busy(true, 'loading');
rcmail.env.mul_items.find(function (object, index) {
if (object.id === id) {
//On met l'objet dans la bonne position après le déplacement
rcmail.env.mul_items.splice(
newIndex,
0,
rcmail.env.mul_items.splice(index, 1)[0],
);
return mel_metapage.Functions.post(
mel_metapage.Functions.url('useful_links', 'update_list'),
{ _list: rcmail.env.mul_items, _key: rcmail.env.mul_items_key },
(data) => {
rcmail.set_busy(false, 'loading', busy);
if (data != 1) {
rcmail.display_message(
"Erreur lors de l'enregistrement",
'error',
);
} else {
if (targetContainer && movedContainer) {
targetContainer.before(movedContainer);
}
}
},
);
}
});
}
/**
* Bind des actions liés à la modale
*/
bindModalActions() {
let self = this;
$(
`${LinkManager.SELECTOR_MODAL_URL}, ${LinkManager.SELECTOR_MODAL_TITLE}`,
).on('change', function () {
if ($(LinkManager.SELECTOR_MODAL_URL).val())
self.displayIcon($(LinkManager.SELECTOR_MODAL_URL).val());
});
$(LinkManager.SELECTOR_MODAL_IMAGE).on('error', function () {
imgError(
$(this).attr('id'),
'no-image',
$(LinkManager.SELECTOR_MODAL_TITLE).val(),
);
});
$(LinkManager.ADD_STORE_BUTTON).on('click', () => {
LinkManager.previsualiser.create_popup("Changer d'icone");
});
MEL_ELASTIC_UI.update_tabs();
}
/**
* Bind des actions au clique droit pour les liens (suppression, modification...)
* @param {string} id on met l'id pour un nouveau lien
*/
bindRightClickActions(id = null) {
let _id = id ? `#link-block-${id}` : '';
// Open the context menu on right-click
$(`${_id}.link-block`).on('contextmenu', function (event) {
event.preventDefault();
if ($(this).hasClass('multilink-open') || $(this).hasClass('sublink'))
return;
const contextMenu = $('#context-menu-' + $(this).data('id'));
if (LinkManager.CONTEXTMENUOPENED) {
LinkManager.CONTEXTMENUOPENED.hide();
}
// Show the context menu
contextMenu.show();
LinkManager.CONTEXTMENUOPENED = contextMenu;
$(document).on('click', function () {
if (
!contextMenu.is(event.target) &&
contextMenu.has(event.target).length === 0
) {
contextMenu.hide();
LinkManager.CONTEXTMENUOPENED = false;
}
});
});
}
/**
* Reset les informations précédentes de la modale
* @param {string} id
* @param {string} title
* @param {string} url
*/
getModalValue(id = null, title = null, url = null, icon = null) {
$(LinkManager.SELECTOR_MODAL_ID).val(id);
$(LinkManager.SELECTOR_MODAL_TITLE).val(title);
$(LinkManager.SELECTOR_MODAL_URL).val(url);
$(LinkManager.SELECTOR_MODAL_ICON).text('');
if (url) {
this.displayIcon(url);
} else {
$(LinkManager.SELECTOR_MODAL_IMAGE).attr('src', '');
$(LinkManager.SELECTOR_MODAL_IMAGE).css('display', 'none');
}
if (icon) {
LinkManager.toggleIcon(icon);
}
if (id) {
$('.add-mel-link').text(rcmail.gettext('update', 'mel_useful_link'));
} else {
$('.add-mel-link').text(rcmail.gettext('add', 'mel_useful_link'));
}
}
/**
* Affiche l'icone du lien
* @param {string} url Url du lien
*/
displayIcon(url) {
$(LinkManager.SELECTOR_MODAL_IMAGE).css('display', 'flex');
$(LinkManager.SELECTOR_MODAL_NO_IMAGE).css('display', 'none');
LinkManager.toggleImage();
const validProtocol = /^https?:\/\//i;
if (!validProtocol.test(url)) {
// Si le protocole n'est pas présent, ajoute 'https://' avant l'URL
url = 'https://' + url;
} else if (url === 'https://') {
url = '';
}
$(LinkManager.SELECTOR_MODAL_URL).val(url);
const apiUrl = LinkManager.fetchIcon(url);
$(LinkManager.SELECTOR_MODAL_IMAGE).attr('src', apiUrl);
}
/**
* Récupère le nom de domaine de l'url pour retourner l'url de l'icone
* @param {string} url Url du lien
* @return {string} Url de l'icone
*/
static fetchIcon(url) {
let domain = '';
try {
domain = new URL(url).hostname;
} catch (error) {
console.error('Erreur :', error.message);
return null;
}
return rcmail.env.external_icon_url + domain;
}
/**
* Helpers functions
*/
/**
* Affiche l'icone et cache l'image
* @param {string} icon
*/
static toggleIcon(icon = null) {
$(LinkManager.SELECTOR_MODAL_IMAGE).addClass('hidden');
$(LinkManager.SELECTOR_MODAL_NO_IMAGE).addClass('hidden');
$(LinkManager.SELECTOR_MODAL_ICON).removeClass('hidden');
if (icon) $(LinkManager.SELECTOR_MODAL_ICON).text(icon);
}
/**
* Affiche l'image et cache l'icone
*/
static toggleImage() {
$(LinkManager.SELECTOR_MODAL_IMAGE).removeClass('hidden');
$(LinkManager.SELECTOR_MODAL_NO_IMAGE).removeClass('hidden');
$(LinkManager.SELECTOR_MODAL_ICON).addClass('hidden');
}
/**
* Supprime le container le plus proche de l'élément passé en paramètre
* @param {HTMLElement} target
*/
removeContainer(target) {
target.closest('.link-block-container').remove();
}
/**
* Vérifie si les champs de la modale ne sont pas vides
* @return {boolean}
*/
checkEmptyInputs() {
const titleInput = $(LinkManager.SELECTOR_MODAL_TITLE);
const urlInput = $(LinkManager.SELECTOR_MODAL_URL);
if (!titleInput.val()) {
titleInput.addClass('error');
return false;
} else {
titleInput.removeClass('error');
}
if (!urlInput.val()) {
urlInput.addClass('error');
return false;
} else {
urlInput.removeClass('error');
}
return true;
}
/**
* Affiche la première lettre si l'image n'est pas trouvée
* @param {string} iconId Id de l'image
* @param {string} iconId Id de l'overlay si l'image n'est pas chargée
* @param {string} title Titre du lien
*/
static imgError(e) {
let $image = $(e.currentTarget).removeAttr('data-src').parent();
const title = $image.parent().attr('title');
console.log('error', e, title);
$(e.currentTarget).addClass('hidden');
$image.find('bnum-icon').addClass('hidden');
$image
.find('span')
.removeClass('hidden')
.text(title?.[0] || '?')
.css({ display: 'block', 'text-align': 'center' });
// let iconImage = $('#' + iconId);
// let noImage = $('#' + noImageId);
// const firstLetter = title
// ? title[0].toUpperCase()
// : $('#mulc-title').val()
// ? $('#mulc-title').val()[0].toUpperCase()
// : null;
// iconImage.hide();
// if (noImage.text() === '') {
// noImage.html(firstLetter);
// }
// noImage.css('display', 'flex');
}
static Start({
displayButtonElement = $('.module_Links .melv2-card-pre'),
} = {}) {
return new LinkManager({ displayButtonElement });
}
}
/**
* @static
* @const
* @type {string}
* @default '#mulc-id'
*/
LinkManager.SELECTOR_MODAL_ID = '#mulc-id';
/**
* @static
* @const
* @type {string}
* @default '#mulc-title'
*/
LinkManager.SELECTOR_MODAL_TITLE = '#mulc-title';
/**
* @static
* @const
* @type {string}
* @default '#folder-mulc-title'
*/
LinkManager.FOLDER_SELECTOR_MODAL_TITLE = '#folder-mulc-title';
/**
* @static
* @const
* @type {string}
* @default '#mulc-url'
*/
LinkManager.SELECTOR_MODAL_URL = '#mulc-url';
/**
* @static
* @const
* @type {string}
* @default '#icon-image'
*/
LinkManager.SELECTOR_MODAL_IMAGE = '#icon-image';
/**
* @static
* @const
* @type {string}
* @default '#no-image'
*/
LinkManager.SELECTOR_MODAL_NO_IMAGE = '#no-image';
/**
* @static
* @const
* @type {string}
* @default '#link-icon'
*/
LinkManager.SELECTOR_MODAL_ICON = '#link-icon';
/**
* @static
* @const
* @type {string}
* @default '#mulba'
*/
LinkManager.CREATE_BUTTON = '#mulba';
/**
* @static
* @const
* @type {string}
* @default '#app_store'
*/
LinkManager.APP_STORE = '#app_store';
/**
* @static
* @const
* @type {string}
* @default '.add-store-link'
*/
LinkManager.ADD_STORE_LINK = '.add-store-link';
/**
* @static
* @const
* @type {string}
* @default '#change_icon'
*/
LinkManager.ADD_STORE_BUTTON = '#change_icon';
/**
* @static
* @const
* @type {string}
* @default '.copy-link'
*/
LinkManager.COPY_LINK = '.copy-link';
/**
* @static
* @const
* @type {string}
* @default '.modify-link'
*/
LinkManager.MODIFY_LINK = '.modify-link';
/**
* @static
* @const
* @type {string}
* @default '.delete-link'
*/
LinkManager.DELETE_LINK = '.delete-link';
/**
* @static
* @const
* @type {string}
* @default '.modify-folder'
*/
LinkManager.MODIFY_FOLDER = '.modify-folder';
/**
* @static
* @const
* @type {string}
* @default '.delete-folder'
*/
LinkManager.DELETE_FOLDER = '.delete-folder';
/**
* @static
* @const
* @type {string}
* @default 'null'
*/
LinkManager.SELECTEDICON = null;
/**
* @static
* @const
* @type {string}
* @default 'null'
*/
LinkManager.DISPLAYIMAGE = null;
/**
* @static
* @const
* @type {string}
* @default 'null'
*/
LinkManager.CONTEXTMENUOPENED = null;
/**
* @static
* @const
* @type {Array}
*/
LinkManager.preview_icon = [
'home',
'settings',
'favorite',
'mail',
'calendar_month',
'forum',
'workspaces',
'folder_open',
'chat',
'call',
'search',
'description',
'folder',
'check',
'check_box',
'verified_user',
'add',
'delete',
'person',
'manage_accounts',
'group',
'contacts',
'share',
'thumb_up',
'public',
'language',
'account_circle',
'info',
'visibility',
'calendar_today',
'schedule',
'help',
'error',
'bookmark',
'notifications',
'edit',
'photo_camera',
'image',
'location_on',
'map',
'explore',
'star',
'apps',
'music_note',
'picture_as_pdf',
'fullscreen',
'terminal',
'file_open',
'create_new_folder',
'token',
'heart_plus',
'monitoring',
'database',
'sell',
'work',
'view_kanban',
'sync_saved_locally',
'eco',
'lock',
];
/**
* @static
* @type {MelIconPrevisualiser}
*/
LinkManager.previsualiser = new MelIconPrevisualiser({
add_default_action_default_buttons: true,
add_defaults_actions: true,
generate_defaults_icons: false,
});
LinkManager.previsualiser.addCustomIcons(LinkManager.preview_icon);
LinkManager.previsualiser.on_create_default_items.push(() => {
let image_url = LinkManager.fetchIcon(
$(LinkManager.SELECTOR_MODAL_URL).val(),
);
return MelHtml.start
.div()
.button({ class: 'image-preview' })
.css({ 'background-image': `url('${image_url}')` })
.attr('onmouseenter', () => {
$('#bnum-folder-main-icon').css({
'background-image': 'url(' + image_url + ')',
'background-size': 'contain',
});
$('#bnum-folder-main-icon .material-symbols-outlined').hide();
})
.attr('onmouseleave', () => {
if (!LinkManager.DISPLAYIMAGE) {
$('#bnum-folder-main-icon').css('background-image', 'none');
$('#bnum-folder-main-icon .material-symbols-outlined').show();
}
})
.attr('onclick', function (e) {
LinkManager.previsualiser._on_default_click(e);
setTimeout(() => {
$('#bnum-folder-main-icon').css({
'background-image': 'url(' + image_url + ')',
'background-size': 'contain',
});
}, 50);
LinkManager.DISPLAYIMAGE = image_url;
})
.icon(' ')
.css({ display: 'none' })
.end()
.end()
.end();
});
LinkManager.previsualiser.on_button_click.push(() => {
LinkManager.DISPLAYIMAGE = null;
if ($('#bnum-folder-main-icon').css('background-image')) {
$('#bnum-folder-main-icon').css('background-image', 'none');
}
});
LinkManager.previsualiser.on_button_hover.push(() => {
if (LinkManager.DISPLAYIMAGE) {
$('#bnum-folder-main-icon').css('background-image', 'none');
}
});
LinkManager.previsualiser.on_button_leave.push(() => {
if (LinkManager.DISPLAYIMAGE) {
$('#bnum-folder-main-icon').css(
'background-image',
'url(' + LinkManager.DISPLAYIMAGE + ')',
);
}
});
LinkManager.previsualiser.on_create_show_selected.push(() => {
return '';
});
LinkManager.previsualiser.on_save.push((popup, $dialog) => {
LinkManager.SELECTEDICON = null;
LinkManager.DISPLAYIMAGE = null;
if (popup.get_selected_icon() !== ' ') {
LinkManager.SELECTEDICON = popup.get_selected_icon();
LinkManager.toggleIcon(LinkManager.SELECTEDICON);
} else {
LinkManager.toggleImage();
}
$($dialog).dialog('close');
});
window.addEventListener('load', function () {
onLoaded();
setTimeout(() => {
onLoaded();
}, 1000);
});
//#region Chargement
/**
* Charge tout les avatars qui ont besoin d'être chargés.
* @package
*/
function onLoaded() {
let imagesToLoad = document.querySelectorAll(
'.link-block-container [data-src]',
);
for (const image of imagesToLoad) {
image.onload = function (pict) {
pict.onload = null;
pict.removeAttribute('data-src');
}.bind(this, image);
image.setAttribute('src', image.dataset.src);
}
window.linksPicturesLoaded = true;
}
//#endregion