All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources._diffs.plugins.videoembed.plugin.js Maven / Gradle / Ivy

/**
 * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

/* eslint-disable @liferay/no-get-data-attribute */

if (!CKEDITOR.plugins.get('videoembed')) {
	const REGEX_HTTP = /^https?/;

	CKEDITOR.DEFAULT_LFR_EMBED_WIDGET_TPL =
		'
{content}
{helpMessageIcon} {helpMessage}

'; /** * Enum for supported embed alignments * @type {Object} */ const EMBED_ALIGNMENT = { CENTER: 'center', LEFT: 'left', RIGHT: 'right', }; /** * Enum values for supported embed alignments * @type {Array} */ const ALIGN_VALUES = [ EMBED_ALIGNMENT.CENTER, EMBED_ALIGNMENT.LEFT, EMBED_ALIGNMENT.RIGHT, ]; /** * Necessary styles for the center alignment * @type {Array.} */ const CENTERED_EMBED_STYLE = [ { name: 'display', value: 'block', }, { name: 'margin-left', value: 'auto', }, { name: 'margin-right', value: 'auto', }, ]; /** * Retrieves the alignment value of an embed element. * * @param {CKEDITOR.dom.element} embed The embed element * @return {String} The alignment value */ const getEmbedAlignment = function (embed) { let embedAlignment = embed.getStyle('float'); if ( !embedAlignment || embedAlignment === 'inherit' || embedAlignment === 'none' ) { embedAlignment = embed.getAttribute('align'); } if (!embedAlignment) { const centeredEmbed = CENTERED_EMBED_STYLE.every((style) => { let styleCheck = embed.getStyle(style.name) === style.value; if (!styleCheck && style.vendorPrefixes) { styleCheck = style.vendorPrefixes.some( (vendorPrefix) => embed.getStyle(vendorPrefix + style.name) === style.value ); } return styleCheck; }); embedAlignment = centeredEmbed ? EMBED_ALIGNMENT.CENTER : null; } return embedAlignment; }; /** * Removes the alignment value of an embed * * @param {CKEDITOR.dom.element} embed The embed element * @param {String} embedAlignment The embed alignment value to be removed */ const removeEmbedAlignment = function (embed, embedAlignment) { if ( embedAlignment === EMBED_ALIGNMENT.LEFT || embedAlignment === EMBED_ALIGNMENT.RIGHT ) { embed.removeStyle('float'); if (embedAlignment === getEmbedAlignment(embed)) { embed.removeAttribute('align'); } } else if (embedAlignment === EMBED_ALIGNMENT.CENTER) { CENTERED_EMBED_STYLE.forEach((style) => { embed.removeStyle(style.name); if (style.vendorPrefixes) { style.vendorPrefixes.forEach((vendorPrefix) => embed.removeStyle(vendorPrefix + style.name) ); } }); } }; /** * Sets the alignment value of an embed * * @param {CKEDITOR.dom.element} embed The embed element * @param {String} embedAlignment The embed alignment value to be set */ const setEmbedAlignment = function (embed, embedAlignment) { removeEmbedAlignment(embed, getEmbedAlignment(embed)); if ( embedAlignment === EMBED_ALIGNMENT.LEFT || embedAlignment === EMBED_ALIGNMENT.RIGHT ) { embed.setStyle('float', embedAlignment); } else if (embedAlignment === EMBED_ALIGNMENT.CENTER) { CENTERED_EMBED_STYLE.forEach((style) => { embed.setStyle(style.name, style.value); if (style.vendorPrefixes) { style.vendorPrefixes.forEach((vendorPrefix) => embed.setStyle(vendorPrefix + style.name, style.value) ); } }); } }; const getSelectedElement = function (editor) { const result = { alignment: null, element: null, }; const selection = editor.getSelection(); if (selection) { const selectedElement = selection.getSelectedElement(); if ( selectedElement && selectedElement.getAttribute('data-cke-widget-wrapper') ) { result.alignment = getEmbedAlignment(selectedElement); result.element = selectedElement; } } return result; }; const resizeElement = function (element, width, height) { const wrapperElement = element.parentElement; if (wrapperElement && width > 0 && height > 0) { wrapperElement.setAttribute('style', `width:${width}px;`); const widgetElement = wrapperElement.querySelector( '[data-widget="videoembed"]' ); if (widgetElement) { const styles = JSON.parse(widgetElement.getAttribute('data-styles')) || {}; styles.width = `${width}px`; styles.height = `${height}px`; widgetElement.setAttribute( 'data-styles', JSON.stringify(styles) ); const iframeElement = widgetElement.querySelector('iframe'); if (iframeElement) { iframeElement.setAttribute('width', width); iframeElement.setAttribute('height', height); } } } }; const selectWidget = function (editor) { requestAnimationFrame(() => { const selection = editor.getSelection(); if (selection) { const wrapperElement = selection.root.find( '[data-cke-widget-wrapper]' ); if (wrapperElement) { const elementList = wrapperElement.$; if (elementList.length) { const lastElement = new CKEDITOR.dom.element( elementList[elementList.length - 1] ); const imageElement = lastElement.findOne('img'); const widgetElement = lastElement.findOne( '[data-widget="videoembed"]' ); if (imageElement && widgetElement) { const range = editor.createRange(); if (!range) { return; } range.setStart(widgetElement, 0); range.setEnd(imageElement, 1); selection.selectRanges([range]); selection.selectElement(lastElement); } } } } }); }; let currentAlignment = null; let currentElement = null; // CSS is added in a compressed form CKEDITOR.addCss( 'img::selection{color:rgba(0,0,0,0)}img.ckimgrsz{outline:1px dashed #000}.ckimgrszwrapper{position:absolute;width:0;height:0;cursor:default;z-index:10001}.ckimgrszwrapper span{display:none;position:absolute;top:0;left:0;width:0;height:0;background-size:100% 100%;opacity:.65;outline:1px dashed #000}.ckimgrszwrapper i{position:absolute;display:block;width:5px;height:5px;background:#fff;border:1px solid #000}.ckimgrszwrapper i.active,.ckimgrszwrapper i:hover{background:#000}.ckimgrszwrapper i.br,.ckimgrszwrapper i.tl{cursor:nwse-resize}.ckimgrszwrapper i.bm,.ckimgrszwrapper i.tm{cursor:ns-resize}.ckimgrszwrapper i.bl,.ckimgrszwrapper i.tr{cursor:nesw-resize}.ckimgrszwrapper i.lm,.ckimgrszwrapper i.rm{cursor:ew-resize}body.dragging-br,body.dragging-br *,body.dragging-tl,body.dragging-tl *{cursor:nwse-resize!important}body.dragging-bm,body.dragging-bm *,body.dragging-tm,body.dragging-tm *{cursor:ns-resize!important}body.dragging-bl,body.dragging-bl *,body.dragging-tr,body.dragging-tr *{cursor:nesw-resize!important}body.dragging-lm,body.dragging-lm *,body.dragging-rm,body.dragging-rm *{cursor:ew-resize!important}' ); /** * CKEditor plugin which adds the infrastructure to embed urls as media objects * * This plugin adds an `videoembed` button that can be used to easily embed a URL and transform it * to an embedded content. * * @class CKEDITOR.plugins.videoembed */ CKEDITOR.plugins.add('videoembed', { _defaultEmbedWidgetUpcastFn(editor, element, data) { let upcastWidget = false; if ( element.name === 'div' && element.attributes['data-embed-url'] ) { data.url = element.attributes['data-embed-url']; upcastWidget = true; } else if ( element.name === 'div' && element.attributes['data-embed-id'] ) { const iframe = element.children[0]; data.url = iframe.attributes.src; delete element.attributes.style; const embedContent = this._generateEmbedContent( editor, data.url, element.getOuterHtml() ); const widgetFragment = new CKEDITOR.htmlParser.fragment.fromHtml(embedContent); upcastWidget = widgetFragment.children[0]; upcastWidget.attributes['data-styles'] = element.attributes['data-styles']; upcastWidget.removeClass('embed-responsive'); upcastWidget.removeClass('embed-responsive-16by9'); element.replaceWith(upcastWidget); } return upcastWidget; }, _generateEmbedContent(editor, url, content) { return this._getWidgetTemplate(editor).output({ content, helpMessage: Liferay.Language.get( 'video-playback-is-disabled-during-edit-mode' ), helpMessageIcon: Liferay.Util.getLexiconIconTpl('info-circle'), url, }); }, _getProviders(editor) { const providers = editor.config.embedProviders || []; return providers.map((provider) => { return { id: provider.id, tpl: new CKEDITOR.template( `
${provider.tpl}
` ), type: provider.type, urlSchemes: provider.urlSchemes.map( (scheme) => new RegExp(scheme) ), }; }); }, _getWidgetTemplate(editor) { return new CKEDITOR.template( editor.config.embedWidgetTpl || CKEDITOR.DEFAULT_LFR_EMBED_WIDGET_TPL ); }, _showError(editor, errorMsg) { Liferay.Util.openToast({ message: errorMsg, type: 'danger', }); setTimeout(() => { editor.getSelection().removeAllRanges(); editor.focus(); editor.resizer.hide(); }, 0); }, afterInit(editor) { editor.on('resize', () => { if (editor.resizer) { editor.resizer.hide(); selectWidget(editor); } }); ALIGN_VALUES.forEach((alignValue) => { const command = editor.getCommand('justify' + alignValue); if (command) { command.on('exec', (event) => { const selectedElement = editor .getSelection() .getSelectedElement(); if ( selectedElement && selectedElement.getAttribute( 'data-cke-widget-wrapper' ) ) { const selectedEmbed = selectedElement.findOne( '[data-widget="videoembed"] [data-embed-id]' ); if (selectedEmbed) { const embedAlignment = getEmbedAlignment(selectedElement); if (embedAlignment === alignValue) { removeEmbedAlignment( selectedElement, alignValue ); } else { setEmbedAlignment( selectedElement, alignValue ); } currentElement = selectedElement; currentAlignment = getEmbedAlignment(selectedElement); const imageElement = selectedElement.findOne('img'); if (imageElement) { editor.resizer.show(imageElement.$); } event.cancel(); const elementPath = new CKEDITOR.dom.elementPath( selectedElement ); ALIGN_VALUES.forEach((alignValue) => { const command = editor.getCommand( 'justify' + alignValue ); if (command) { command.refresh(editor, elementPath); } }); } } }); command.on('refresh', (event) => { const lastElement = event.data.path.lastElement; if ( lastElement && lastElement.getAttribute( 'data-cke-widget-wrapper' ) && lastElement.findOne('[data-widget] [data-embed-id]') ) { const embedAlignment = getEmbedAlignment(lastElement); event.sender.setState( embedAlignment === alignValue ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF ); event.cancel(); } }); } }); }, getValidProvider(editor, url, type) { const validProvider = this._getProviders(editor) .filter((provider) => { return type ? provider.type === type : true; }) .find((provider) => { const scheme = provider.urlSchemes.find((scheme) => scheme.test(url) ); if (scheme) { return provider; } }); return validProvider; }, init(editor) { const instance = this; editor.widgets.add('videoembed', { data(event) { const instance = this; const stylesJSON = instance.element.getAttribute('data-styles'); let styles = stylesJSON ? JSON.parse(stylesJSON) : null; if (!styles) { const iframe = instance.wrapper.findOne('iframe'); const bounds = instance.wrapper.$.getBoundingClientRect(); const width = iframe.getAttribute('width'); const pwidth = width >= bounds.width ? 100 : Math.round((width / bounds.width) * 100); styles = { width: `${pwidth}%`, }; } instance.wrapper.setAttribute( 'style', CKEDITOR.tools.writeCssText(styles) ); const doc = instance.wrapper.getDocument(); function mouseDownListener(event) { const result = getSelectedElement(editor); currentAlignment = result.alignment; currentElement = result.element; if (editor.resizer.isHandle(event.target)) { editor.resizer.initDrag(event); } } doc.$.addEventListener('mousedown', mouseDownListener); const body = doc.getBody(); if (body) { body.addClass('cke_editor_content'); } if (editor._selectEmbedWidget === event.data.url) { selectWidget(editor); } }, downcast(widget) { const embedContent = widget.children[0]; embedContent.attributes.class = 'embed-responsive embed-responsive-16by9'; embedContent.attributes['data-styles'] = JSON.stringify( CKEDITOR.tools.parseCssText( widget.parent.attributes.style ) ); embedContent.attributes.style = widget.parent.attributes.style; return embedContent; }, draggable: false, mask: true, requiredContent: 'div[data-embed-url]', upcast(element, data) { const embedWidgetUpcastFn = editor.config.embedWidgetUpcastFn || instance._defaultEmbedWidgetUpcastFn.bind(instance); return embedWidgetUpcastFn(editor, element, data); }, }); editor.addCommand( 'videoembed', new CKEDITOR.dialogCommand('videoembedDialog') ); if (editor.ui.addButton) { editor.ui.addButton('VideoEmbed', { command: 'videoembed', icon: instance.path + 'icons/video.png', label: Liferay.Language.get('video'), }); } CKEDITOR.dialog.add( 'videoembedDialog', instance.path + 'dialogs/videoembedDialog.js' ); window.addEventListener( 'resize', () => { editor.resizer.hide(); selectWidget(editor); }, false ); editor.on('selectionChange', (_event) => { const selection = editor.getSelection(); if (selection) { const element = selection.getSelectedElement(); if (element) { const widgetElement = element.findOne( '[data-widget="videoembed"]' ); if (widgetElement) { const scrollPosition = new CKEDITOR.dom.window( window ).getScrollPosition(); const region = element.getClientRect(); region.direction = CKEDITOR.SELECTION_BOTTOM_TO_TOP; region.left -= scrollPosition.x; region.top += scrollPosition.y; editor.fire('editorInteraction', { nativeEvent: {}, selectionData: { element: widgetElement, region, }, }); } const imageElement = element.findOne( 'img.cke_widget_mask' ); if (imageElement) { editor.resizer.show(imageElement.$); } } else { if (editor.resizer) { editor.resizer.hide(); } } } }); editor.on('blur', () => { editor.resizer.hide(); }); editor.filter.addElementCallback((element) => { if ('data-embed-url' in element.attributes) { return CKEDITOR.FILTER_SKIP_TREE; } }); const path = instance.path; const dependencies = [ CKEDITOR.getUrl(path + 'DragEvent.es.js'), CKEDITOR.getUrl(path + 'Resizer.es.js'), ]; editor.on('dataReady', () => { CKEDITOR.scriptLoader.load(dependencies, () => { editor.resizer = new Liferay.ResizerCKEditor(editor, { onComplete(element, width, height) { resizeElement(element, width, height); if (currentAlignment && currentElement) { setEmbedAlignment( currentElement, currentAlignment ); } selectWidget(editor); }, }); }); }); }, onOkVideo(editor, data) { const type = data.type; const url = data.url; let content; if (REGEX_HTTP.test(url)) { const provider = this.getValidProvider(editor, url, type); if (provider) { const schemeProvider = provider.urlSchemes.find((scheme) => scheme.test(url) ); if (schemeProvider) { const embedId = schemeProvider.exec(url)[1]; content = provider.tpl.output({ embedId, }); } editor._selectEmbedWidget = url; const embedContent = this._generateEmbedContent( editor, url, content ); editor.insertHtml(embedContent); } else { this._showError( editor, Liferay.Language.get( 'sorry,-this-platform-is-not-supported' ) ); } } else { this._showError( editor, Liferay.Language.get('enter-a-valid-url') ); } }, onOkVideoHtml(editor, html, url) { const embedContent = this._generateEmbedContent( editor, url, `
${html}
` ); editor.insertHtml(embedContent); }, requires: 'widget', }); }