2020-01-26 16:21:20 +01:00
|
|
|
/**
|
2022-04-02 16:28:01 +02:00
|
|
|
* @copyright 2022, the Converse.js contributors
|
2020-01-26 16:21:20 +01:00
|
|
|
* @license Mozilla Public License (MPLv2)
|
|
|
|
* @description This is the DOM/HTML utilities module.
|
|
|
|
*/
|
2021-06-17 12:31:38 +02:00
|
|
|
import isFunction from 'lodash-es/isFunction';
|
2020-01-09 10:34:39 +01:00
|
|
|
import log from '@converse/headless/log';
|
2021-06-17 12:31:38 +02:00
|
|
|
import tpl_audio from 'templates/audio.js';
|
|
|
|
import tpl_file from 'templates/file.js';
|
|
|
|
import tpl_form_captcha from '../templates/form_captcha.js';
|
|
|
|
import tpl_form_checkbox from '../templates/form_checkbox.js';
|
|
|
|
import tpl_form_help from '../templates/form_help.js';
|
|
|
|
import tpl_form_input from '../templates/form_input.js';
|
|
|
|
import tpl_form_select from '../templates/form_select.js';
|
|
|
|
import tpl_form_textarea from '../templates/form_textarea.js';
|
|
|
|
import tpl_form_url from '../templates/form_url.js';
|
|
|
|
import tpl_form_username from '../templates/form_username.js';
|
|
|
|
import tpl_hyperlink from 'templates/hyperlink.js';
|
2021-06-24 20:20:02 +02:00
|
|
|
import tpl_video from 'templates/video.js';
|
2021-06-17 12:31:38 +02:00
|
|
|
import u from '../headless/utils/core';
|
2021-07-05 17:15:48 +02:00
|
|
|
import { converse } from '@converse/headless/core';
|
|
|
|
import { getURI, isAudioURL, isImageURL, isVideoURL } from '@converse/headless/utils/url.js';
|
2021-06-17 12:31:38 +02:00
|
|
|
import { render } from 'lit';
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2020-12-10 16:39:27 +01:00
|
|
|
const { sizzle } = converse.env;
|
|
|
|
|
2020-05-07 19:35:15 +02:00
|
|
|
const APPROVED_URL_PROTOCOLS = ['http', 'https', 'xmpp', 'mailto'];
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-04-24 11:03:27 +02:00
|
|
|
function getAutoCompleteProperty (name, options) {
|
|
|
|
return {
|
|
|
|
'muc#roomconfig_lang': 'language',
|
2020-04-15 13:59:55 +02:00
|
|
|
'muc#roomconfig_roomsecret': options?.new_password ? 'new-password' : 'current-password'
|
2019-04-24 11:03:27 +02:00
|
|
|
}[name];
|
|
|
|
}
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
const XFORM_TYPE_MAP = {
|
|
|
|
'text-private': 'password',
|
|
|
|
'text-single': 'text',
|
|
|
|
'fixed': 'label',
|
|
|
|
'boolean': 'checkbox',
|
|
|
|
'hidden': 'hidden',
|
|
|
|
'jid-multi': 'textarea',
|
|
|
|
'list-single': 'dropdown',
|
|
|
|
'list-multi': 'dropdown'
|
|
|
|
};
|
|
|
|
|
2019-10-23 20:40:52 +02:00
|
|
|
const XFORM_VALIDATE_TYPE_MAP = {
|
2021-10-30 20:56:17 +02:00
|
|
|
'xs:anyURI': 'url',
|
|
|
|
'xs:byte': 'number',
|
|
|
|
'xs:date': 'date',
|
|
|
|
'xs:dateTime': 'datetime',
|
|
|
|
'xs:int': 'number',
|
|
|
|
'xs:integer': 'number',
|
|
|
|
'xs:time': 'time',
|
2019-10-23 20:40:52 +02:00
|
|
|
}
|
|
|
|
|
2019-10-23 16:39:51 +02:00
|
|
|
function getInputType(field) {
|
2021-10-30 20:56:17 +02:00
|
|
|
const type = XFORM_TYPE_MAP[field.getAttribute('type')]
|
|
|
|
if (type == 'text') {
|
|
|
|
const datatypes = field.getElementsByTagNameNS("http://jabber.org/protocol/xdata-validate", "validate");
|
|
|
|
if (datatypes.length === 1) {
|
|
|
|
const datatype = datatypes[0].getAttribute("datatype");
|
|
|
|
return XFORM_VALIDATE_TYPE_MAP[datatype] || type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return type;
|
2019-10-23 16:39:51 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
function slideOutWrapup (el) {
|
|
|
|
/* Wrapup function for slideOut. */
|
|
|
|
el.removeAttribute('data-slider-marker');
|
|
|
|
el.classList.remove('collapsed');
|
2021-06-17 12:31:38 +02:00
|
|
|
el.style.overflow = '';
|
|
|
|
el.style.height = '';
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
|
2022-08-21 13:03:32 +02:00
|
|
|
export function getFileName (url) {
|
|
|
|
const uri = getURI(url);
|
2019-12-29 21:54:01 +01:00
|
|
|
try {
|
2020-01-09 10:34:39 +01:00
|
|
|
return decodeURI(uri.filename());
|
2019-12-29 21:54:01 +01:00
|
|
|
} catch (error) {
|
2020-01-09 10:34:39 +01:00
|
|
|
log.debug(error);
|
|
|
|
return uri.filename();
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2020-01-09 10:34:39 +01:00
|
|
|
}
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2020-01-09 10:34:39 +01:00
|
|
|
/**
|
|
|
|
* Returns the markup for a URL that points to a downloadable asset
|
|
|
|
* (such as a video, image or audio file).
|
|
|
|
* @method u#getOOBURLMarkup
|
|
|
|
* @param { String } url
|
|
|
|
* @returns { String }
|
|
|
|
*/
|
2021-07-01 12:57:28 +02:00
|
|
|
export function getOOBURLMarkup (url) {
|
2020-01-09 10:34:39 +01:00
|
|
|
const uri = getURI(url);
|
|
|
|
if (uri === null) {
|
|
|
|
return url;
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2021-07-05 17:15:48 +02:00
|
|
|
if (isVideoURL(uri)) {
|
2021-06-17 15:48:03 +02:00
|
|
|
return tpl_video(url);
|
2021-07-05 17:15:48 +02:00
|
|
|
} else if (isAudioURL(uri)) {
|
2021-06-24 20:20:02 +02:00
|
|
|
return tpl_audio(url);
|
2021-07-05 17:15:48 +02:00
|
|
|
} else if (isImageURL(uri)) {
|
2021-06-24 20:20:02 +02:00
|
|
|
return tpl_file(uri.toString(), getFileName(uri));
|
2020-01-09 10:34:39 +01:00
|
|
|
} else {
|
2021-06-24 20:20:02 +02:00
|
|
|
return tpl_file(uri.toString(), getFileName(uri));
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2021-07-01 12:57:28 +02:00
|
|
|
}
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2020-09-26 21:07:46 +02:00
|
|
|
/**
|
|
|
|
* Return the height of the passed in DOM element,
|
|
|
|
* based on the heights of its children.
|
|
|
|
* @method u#calculateElementHeight
|
|
|
|
* @param {HTMLElement} el
|
|
|
|
* @returns {integer}
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
u.calculateElementHeight = function (el) {
|
2020-03-06 14:38:32 +01:00
|
|
|
return Array.from(el.children).reduce((result, child) => result + child.offsetHeight, 0);
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.getNextElement = function (el, selector = '*') {
|
2018-10-23 03:41:38 +02:00
|
|
|
let next_el = el.nextElementSibling;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (next_el !== null && !sizzle.matchesSelector(next_el, selector)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
next_el = next_el.nextElementSibling;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return next_el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.getPreviousElement = function (el, selector = '*') {
|
2019-08-05 01:39:57 +02:00
|
|
|
let prev_el = el.previousElementSibling;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (prev_el !== null && !sizzle.matchesSelector(prev_el, selector)) {
|
2021-06-17 12:31:38 +02:00
|
|
|
prev_el = prev_el.previousElementSibling;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return prev_el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.getFirstChildElement = function (el, selector = '*') {
|
2018-10-23 03:41:38 +02:00
|
|
|
let first_el = el.firstElementChild;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (first_el !== null && !sizzle.matchesSelector(first_el, selector)) {
|
2021-06-17 12:31:38 +02:00
|
|
|
first_el = first_el.nextElementSibling;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return first_el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.getLastChildElement = function (el, selector = '*') {
|
2018-10-23 03:41:38 +02:00
|
|
|
let last_el = el.lastElementChild;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (last_el !== null && !sizzle.matchesSelector(last_el, selector)) {
|
2021-06-17 12:31:38 +02:00
|
|
|
last_el = last_el.previousElementSibling;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return last_el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
u.hasClass = function (className, el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
return el instanceof Element && el.classList.contains(className);
|
2018-10-23 03:41:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2020-02-10 11:23:55 +01:00
|
|
|
u.toggleClass = function (className, el) {
|
|
|
|
u.hasClass(className, el) ? u.removeClass(className, el) : u.addClass(className, el);
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2020-02-10 11:23:55 +01:00
|
|
|
|
2019-09-11 15:23:58 +02:00
|
|
|
/**
|
|
|
|
* Add a class to an element.
|
|
|
|
* @method u#addClass
|
|
|
|
* @param {string} className
|
|
|
|
* @param {Element} el
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
u.addClass = function (className, el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
el instanceof Element && el.classList.add(className);
|
2019-08-05 01:39:57 +02:00
|
|
|
return el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2019-09-11 15:23:58 +02:00
|
|
|
/**
|
|
|
|
* Remove a class from an element.
|
|
|
|
* @method u#removeClass
|
|
|
|
* @param {string} className
|
|
|
|
* @param {Element} el
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
u.removeClass = function (className, el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
el instanceof Element && el.classList.remove(className);
|
2018-10-23 03:41:38 +02:00
|
|
|
return el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
u.removeElement = function (el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
el instanceof Element && el.parentNode && el.parentNode.removeChild(el);
|
2019-08-05 01:39:57 +02:00
|
|
|
return el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2020-09-26 09:51:44 +02:00
|
|
|
u.getElementFromTemplateResult = function (tr) {
|
|
|
|
const div = document.createElement('div');
|
|
|
|
render(tr, div);
|
|
|
|
return div.firstElementChild;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2020-09-26 09:51:44 +02:00
|
|
|
|
2020-03-06 14:38:32 +01:00
|
|
|
u.showElement = el => {
|
|
|
|
u.removeClass('collapsed', el);
|
|
|
|
u.removeClass('hidden', el);
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
u.hideElement = function (el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
el instanceof Element && el.classList.add('hidden');
|
2018-10-23 03:41:38 +02:00
|
|
|
return el;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2022-01-31 21:51:51 +01:00
|
|
|
export function ancestor (el, selector) {
|
2018-10-23 03:41:38 +02:00
|
|
|
let parent = el;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (parent !== null && !sizzle.matchesSelector(parent, selector)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
parent = parent.parentElement;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return parent;
|
2022-01-31 21:51:51 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-10-09 16:01:38 +02:00
|
|
|
/**
|
|
|
|
* Return the element's siblings until one matches the selector.
|
|
|
|
* @private
|
|
|
|
* @method u#nextUntil
|
2019-09-24 15:33:41 +02:00
|
|
|
* @param { HTMLElement } el
|
|
|
|
* @param { String } selector
|
2019-10-09 16:01:38 +02:00
|
|
|
*/
|
|
|
|
u.nextUntil = function (el, selector) {
|
2018-10-23 03:41:38 +02:00
|
|
|
const matches = [];
|
|
|
|
let sibling_el = el.nextElementSibling;
|
2019-08-13 19:30:20 +02:00
|
|
|
while (sibling_el !== null && !sibling_el.matches(selector)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
matches.push(sibling_el);
|
|
|
|
sibling_el = sibling_el.nextElementSibling;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return matches;
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-03-29 23:47:56 +01:00
|
|
|
/**
|
|
|
|
* Helper method that replace HTML-escaped symbols with equivalent characters
|
|
|
|
* (e.g. transform occurrences of '&' to '&')
|
|
|
|
* @private
|
|
|
|
* @method u#unescapeHTML
|
|
|
|
* @param { String } string - a String containing the HTML-escaped symbols.
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
u.unescapeHTML = function (string) {
|
|
|
|
var div = document.createElement('div');
|
|
|
|
div.innerHTML = string;
|
|
|
|
return div.innerText;
|
|
|
|
};
|
|
|
|
|
|
|
|
u.escapeHTML = function (string) {
|
|
|
|
return string
|
2021-06-17 12:31:38 +02:00
|
|
|
.replace(/&/g, '&')
|
|
|
|
.replace(/</g, '<')
|
|
|
|
.replace(/>/g, '>')
|
|
|
|
.replace(/"/g, '"');
|
2020-05-05 02:59:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
function isProtocolApproved (protocol, safeProtocolsList = APPROVED_URL_PROTOCOLS) {
|
|
|
|
return !!safeProtocolsList.includes(protocol);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Will return false if URL is malformed or contains disallowed characters
|
|
|
|
function isUrlValid (urlString) {
|
|
|
|
try {
|
|
|
|
const url = new URL(urlString);
|
|
|
|
return !!url;
|
|
|
|
} catch (error) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-01-09 10:34:39 +01:00
|
|
|
}
|
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
export function getHyperlinkTemplate (url) {
|
2020-05-15 14:33:31 +02:00
|
|
|
const http_url = RegExp('^w{3}.', 'ig').test(url) ? `http://${url}` : url;
|
2020-05-05 02:59:36 +02:00
|
|
|
const uri = getURI(url);
|
2020-05-15 14:33:31 +02:00
|
|
|
if (uri !== null && isUrlValid(http_url) && (isProtocolApproved(uri._parts.protocol) || !uri._parts.protocol)) {
|
2021-06-17 12:31:38 +02:00
|
|
|
return tpl_hyperlink(uri, url);
|
2020-05-05 02:59:36 +02:00
|
|
|
}
|
|
|
|
return url;
|
2021-06-17 12:31:38 +02:00
|
|
|
}
|
2020-01-09 10:34:39 +01:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.slideInAllElements = function (elements, duration = 300) {
|
2020-03-06 14:38:32 +01:00
|
|
|
return Promise.all(Array.from(elements).map(e => u.slideIn(e, duration)));
|
2018-10-23 03:41:38 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
u.slideToggleElement = function (el, duration) {
|
2020-03-06 14:38:32 +01:00
|
|
|
if (u.hasClass('collapsed', el) || u.hasClass('hidden', el)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return u.slideOut(el, duration);
|
|
|
|
} else {
|
|
|
|
return u.slideIn(el, duration);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-29 23:47:56 +01:00
|
|
|
/**
|
|
|
|
* Shows/expands an element by sliding it out of itself
|
|
|
|
* @private
|
|
|
|
* @method u#slideOut
|
|
|
|
* @param { HTMLElement } el - The HTML string
|
|
|
|
* @param { Number } duration - The duration amount in milliseconds
|
|
|
|
*/
|
2021-06-17 12:31:38 +02:00
|
|
|
u.slideOut = function (el, duration = 200) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
2019-08-05 01:39:57 +02:00
|
|
|
if (!el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
const err = 'An element needs to be passed in to slideOut';
|
2020-01-09 10:34:39 +01:00
|
|
|
log.warn(err);
|
2018-10-23 03:41:38 +02:00
|
|
|
reject(new Error(err));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const marker = el.getAttribute('data-slider-marker');
|
|
|
|
if (marker) {
|
|
|
|
el.removeAttribute('data-slider-marker');
|
|
|
|
window.cancelAnimationFrame(marker);
|
|
|
|
}
|
|
|
|
const end_height = u.calculateElementHeight(el);
|
2021-06-17 12:31:38 +02:00
|
|
|
if (window.converse_disable_effects) {
|
|
|
|
// Effects are disabled (for tests)
|
2018-10-23 03:41:38 +02:00
|
|
|
el.style.height = end_height + 'px';
|
|
|
|
slideOutWrapup(el);
|
|
|
|
resolve();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!u.hasClass('collapsed', el) && !u.hasClass('hidden', el)) {
|
|
|
|
resolve();
|
|
|
|
return;
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
const steps = duration / 17; // We assume 17ms per animation which is ~60FPS
|
2018-10-23 03:41:38 +02:00
|
|
|
let height = 0;
|
|
|
|
|
|
|
|
function draw () {
|
2021-06-17 12:31:38 +02:00
|
|
|
height += end_height / steps;
|
2018-10-23 03:41:38 +02:00
|
|
|
if (height < end_height) {
|
|
|
|
el.style.height = height + 'px';
|
2021-06-17 12:31:38 +02:00
|
|
|
el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw));
|
2018-10-23 03:41:38 +02:00
|
|
|
} else {
|
|
|
|
// We recalculate the height to work around an apparent
|
|
|
|
// browser bug where browsers don't know the correct
|
|
|
|
// offsetHeight beforehand.
|
2018-10-22 13:59:06 +02:00
|
|
|
el.removeAttribute('data-slider-marker');
|
2018-10-23 03:41:38 +02:00
|
|
|
el.style.height = u.calculateElementHeight(el) + 'px';
|
2021-06-17 12:31:38 +02:00
|
|
|
el.style.overflow = '';
|
|
|
|
el.style.height = '';
|
2018-10-23 03:41:38 +02:00
|
|
|
resolve();
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
el.style.height = '0';
|
|
|
|
el.style.overflow = 'hidden';
|
|
|
|
el.classList.remove('hidden');
|
|
|
|
el.classList.remove('collapsed');
|
2021-06-17 12:31:38 +02:00
|
|
|
el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw));
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
|
|
|
};
|
2018-10-22 13:59:06 +02:00
|
|
|
|
2021-06-17 12:31:38 +02:00
|
|
|
u.slideIn = function (el, duration = 200) {
|
2018-10-23 03:41:38 +02:00
|
|
|
/* Hides/collapses an element by sliding it into itself. */
|
|
|
|
return new Promise((resolve, reject) => {
|
2019-08-05 01:39:57 +02:00
|
|
|
if (!el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
const err = 'An element needs to be passed in to slideIn';
|
2020-01-09 10:34:39 +01:00
|
|
|
log.warn(err);
|
2018-10-23 03:41:38 +02:00
|
|
|
return reject(new Error(err));
|
2020-03-06 14:38:32 +01:00
|
|
|
} else if (u.hasClass('collapsed', el)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return resolve(el);
|
2021-06-17 12:31:38 +02:00
|
|
|
} else if (window.converse_disable_effects) {
|
|
|
|
// Effects are disabled (for tests)
|
2018-10-23 03:41:38 +02:00
|
|
|
el.classList.add('collapsed');
|
2021-06-17 12:31:38 +02:00
|
|
|
el.style.height = '';
|
2018-10-23 03:41:38 +02:00
|
|
|
return resolve(el);
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
const marker = el.getAttribute('data-slider-marker');
|
|
|
|
if (marker) {
|
|
|
|
el.removeAttribute('data-slider-marker');
|
|
|
|
window.cancelAnimationFrame(marker);
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
const original_height = el.offsetHeight,
|
2021-06-17 12:31:38 +02:00
|
|
|
steps = duration / 17; // We assume 17ms per animation which is ~60FPS
|
2018-10-23 03:41:38 +02:00
|
|
|
let height = original_height;
|
|
|
|
|
|
|
|
el.style.overflow = 'hidden';
|
|
|
|
|
|
|
|
function draw () {
|
2021-06-17 12:31:38 +02:00
|
|
|
height -= original_height / steps;
|
2018-10-23 03:41:38 +02:00
|
|
|
if (height > 0) {
|
|
|
|
el.style.height = height + 'px';
|
2021-06-17 12:31:38 +02:00
|
|
|
el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw));
|
2018-10-22 13:59:06 +02:00
|
|
|
} else {
|
2018-10-23 03:41:38 +02:00
|
|
|
el.removeAttribute('data-slider-marker');
|
|
|
|
el.classList.add('collapsed');
|
2021-06-17 12:31:38 +02:00
|
|
|
el.style.height = '';
|
2018-10-23 03:41:38 +02:00
|
|
|
resolve(el);
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2021-06-17 12:31:38 +02:00
|
|
|
el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw));
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
function afterAnimationEnds (el, callback) {
|
|
|
|
el.classList.remove('visible');
|
2020-03-06 14:38:32 +01:00
|
|
|
if (isFunction(callback)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
callback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 11:28:28 +02:00
|
|
|
u.isInDOM = function (el) {
|
|
|
|
return document.querySelector('body').contains(el);
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
2019-09-11 11:28:28 +02:00
|
|
|
|
2018-10-26 10:13:43 +02:00
|
|
|
u.isVisible = function (el) {
|
2019-10-29 11:30:31 +01:00
|
|
|
if (el === null) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-26 10:13:43 +02:00
|
|
|
if (u.hasClass('hidden', el)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// XXX: Taken from jQuery's "visible" implementation
|
|
|
|
return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
|
|
|
|
};
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
u.fadeIn = function (el, callback) {
|
2019-08-05 01:39:57 +02:00
|
|
|
if (!el) {
|
2021-06-17 12:31:38 +02:00
|
|
|
log.warn('An element needs to be passed in to fadeIn');
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
if (window.converse_disable_effects) {
|
|
|
|
el.classList.remove('hidden');
|
|
|
|
return afterAnimationEnds(el, callback);
|
|
|
|
}
|
2020-03-06 14:38:32 +01:00
|
|
|
if (u.hasClass('hidden', el)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
el.classList.add('visible');
|
|
|
|
el.classList.remove('hidden');
|
2021-06-17 12:31:38 +02:00
|
|
|
el.addEventListener('webkitAnimationEnd', () => afterAnimationEnds(el, callback));
|
|
|
|
el.addEventListener('animationend', () => afterAnimationEnds(el, callback));
|
|
|
|
el.addEventListener('oanimationend', () => afterAnimationEnds(el, callback));
|
2018-10-23 03:41:38 +02:00
|
|
|
} else {
|
|
|
|
afterAnimationEnds(el, callback);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-29 23:47:56 +01:00
|
|
|
/**
|
2020-12-28 17:48:25 +01:00
|
|
|
* Takes an XML field in XMPP XForm (XEP-004: Data Forms) format returns a
|
2021-04-14 22:56:59 +02:00
|
|
|
* [TemplateResult](https://lit.polymer-project.org/api/classes/_lit_html_.templateresult.html).
|
2020-12-28 17:48:25 +01:00
|
|
|
* @method u#xForm2TemplateResult
|
2019-03-29 23:47:56 +01:00
|
|
|
* @param { XMLElement } field - the field to convert
|
2020-12-28 17:48:25 +01:00
|
|
|
* @param { XMLElement } stanza - the containing stanza
|
|
|
|
* @param { Object } options
|
|
|
|
* @returns { TemplateResult }
|
2019-03-29 23:47:56 +01:00
|
|
|
*/
|
2020-12-28 17:48:25 +01:00
|
|
|
u.xForm2TemplateResult = function (field, stanza, options) {
|
2021-06-17 12:31:38 +02:00
|
|
|
if (field.getAttribute('type') === 'list-single' || field.getAttribute('type') === 'list-multi') {
|
2020-03-24 12:26:34 +01:00
|
|
|
const values = u.queryChildren(field, 'value').map(el => el?.textContent);
|
2020-03-06 14:38:32 +01:00
|
|
|
const options = u.queryChildren(field, 'option').map(option => {
|
2020-03-24 12:26:34 +01:00
|
|
|
const value = option.querySelector('value')?.textContent;
|
2020-12-28 17:48:25 +01:00
|
|
|
return {
|
2020-03-06 14:38:32 +01:00
|
|
|
'value': value,
|
|
|
|
'label': option.getAttribute('label'),
|
|
|
|
'selected': values.includes(value),
|
|
|
|
'required': !!field.querySelector('required')
|
2020-12-28 17:48:25 +01:00
|
|
|
};
|
2020-03-06 14:38:32 +01:00
|
|
|
});
|
2019-03-04 21:54:53 +01:00
|
|
|
return tpl_form_select({
|
2020-12-28 17:48:25 +01:00
|
|
|
options,
|
2019-03-04 21:54:53 +01:00
|
|
|
'id': u.getUniqueId(),
|
|
|
|
'label': field.getAttribute('label'),
|
2021-06-17 12:31:38 +02:00
|
|
|
'multiple': field.getAttribute('type') === 'list-multi',
|
2020-12-28 17:48:25 +01:00
|
|
|
'name': field.getAttribute('var'),
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required')
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
|
|
|
} else if (field.getAttribute('type') === 'fixed') {
|
2020-03-24 12:26:34 +01:00
|
|
|
const text = field.querySelector('value')?.textContent;
|
2021-06-17 12:31:38 +02:00
|
|
|
return tpl_form_help({ text });
|
2019-03-04 21:54:53 +01:00
|
|
|
} else if (field.getAttribute('type') === 'jid-multi') {
|
|
|
|
return tpl_form_textarea({
|
|
|
|
'name': field.getAttribute('var'),
|
|
|
|
'label': field.getAttribute('label') || '',
|
2020-03-24 12:26:34 +01:00
|
|
|
'value': field.querySelector('value')?.textContent,
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required')
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
|
|
|
} else if (field.getAttribute('type') === 'boolean') {
|
2020-05-31 11:13:32 +02:00
|
|
|
const value = field.querySelector('value')?.textContent;
|
2019-03-04 21:54:53 +01:00
|
|
|
return tpl_form_checkbox({
|
|
|
|
'id': u.getUniqueId(),
|
|
|
|
'name': field.getAttribute('var'),
|
|
|
|
'label': field.getAttribute('label') || '',
|
2021-06-17 12:31:38 +02:00
|
|
|
'checked': ((value === '1' || value === 'true') && 'checked="1"') || '',
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required')
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
|
|
|
} else if (field.getAttribute('var') === 'url') {
|
|
|
|
return tpl_form_url({
|
|
|
|
'label': field.getAttribute('label') || '',
|
2020-03-24 12:26:34 +01:00
|
|
|
'value': field.querySelector('value')?.textContent
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
|
|
|
} else if (field.getAttribute('var') === 'username') {
|
|
|
|
return tpl_form_username({
|
2021-06-17 12:31:38 +02:00
|
|
|
'domain': ' @' + options.domain,
|
2019-03-04 21:54:53 +01:00
|
|
|
'name': field.getAttribute('var'),
|
2019-10-23 16:39:51 +02:00
|
|
|
'type': getInputType(field),
|
2019-03-04 21:54:53 +01:00
|
|
|
'label': field.getAttribute('label') || '',
|
2020-03-24 12:26:34 +01:00
|
|
|
'value': field.querySelector('value')?.textContent,
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required')
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
2021-06-17 12:31:38 +02:00
|
|
|
} else if (field.getAttribute('var') === 'ocr') {
|
|
|
|
// Captcha
|
2019-03-04 21:54:53 +01:00
|
|
|
const uri = field.querySelector('uri');
|
2021-06-17 12:31:38 +02:00
|
|
|
const el = sizzle('data[cid="' + uri.textContent.replace(/^cid:/, '') + '"]', stanza)[0];
|
2019-03-04 21:54:53 +01:00
|
|
|
return tpl_form_captcha({
|
|
|
|
'label': field.getAttribute('label'),
|
|
|
|
'name': field.getAttribute('var'),
|
2020-03-24 12:26:34 +01:00
|
|
|
'data': el?.textContent,
|
2019-03-04 21:54:53 +01:00
|
|
|
'type': uri.getAttribute('type'),
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required')
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
2018-10-23 03:41:38 +02:00
|
|
|
} else {
|
2019-04-24 11:03:27 +02:00
|
|
|
const name = field.getAttribute('var');
|
2019-03-04 21:54:53 +01:00
|
|
|
return tpl_form_input({
|
|
|
|
'id': u.getUniqueId(),
|
|
|
|
'label': field.getAttribute('label') || '',
|
2019-04-24 11:03:27 +02:00
|
|
|
'name': name,
|
2020-04-15 13:59:55 +02:00
|
|
|
'fixed_username': options?.fixed_username,
|
2019-04-24 11:03:27 +02:00
|
|
|
'autocomplete': getAutoCompleteProperty(name, options),
|
2019-03-04 21:54:53 +01:00
|
|
|
'placeholder': null,
|
2019-08-05 01:39:57 +02:00
|
|
|
'required': !!field.querySelector('required'),
|
2019-10-23 16:39:51 +02:00
|
|
|
'type': getInputType(field),
|
2020-03-24 12:26:34 +01:00
|
|
|
'value': field.querySelector('value')?.textContent
|
2019-03-04 21:54:53 +01:00
|
|
|
});
|
2018-10-22 13:59:06 +02:00
|
|
|
}
|
2021-06-17 12:31:38 +02:00
|
|
|
};
|
|
|
|
|
2022-01-31 21:51:51 +01:00
|
|
|
Object.assign(u, { getOOBURLMarkup, ancestor });
|
2020-12-28 17:48:25 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
export default u;
|