Skip to content

Commit d89a2fd

Browse files
committed
Lexical: Added media src conversions
Only actuall added YT in the end. Google had changed URL scheme, and Vimeo seems to just be something else now, can't really browse video pages like before.
1 parent 958b537 commit d89a2fd

File tree

4 files changed

+79
-22
lines changed

4 files changed

+79
-22
lines changed

resources/js/wysiwyg/lexical/rich-text/LexicalMediaNode.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from "lexical/nodes/common";
1717
import {$selectSingleNode} from "../../utils/selection";
1818
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
19+
import * as url from "node:url";
1920

2021
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
2122
export type MediaNodeSource = {
@@ -343,11 +344,55 @@ export function $createMediaNodeFromHtml(html: string): MediaNode | null {
343344
return domElementToNode(tag as MediaNodeTag, el);
344345
}
345346

347+
interface UrlPattern {
348+
readonly regex: RegExp;
349+
readonly w: number;
350+
readonly h: number;
351+
readonly url: string;
352+
}
353+
354+
/**
355+
* These patterns originate from the tinymce/tinymce project.
356+
* https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts
357+
* License: MIT Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
358+
* License Link: https://github.com/tinymce/tinymce/blob/584a150679669859a528828e5d2910a083b1d911/LICENSE.TXT
359+
*/
360+
const urlPatterns: UrlPattern[] = [
361+
{
362+
regex: /.*?youtu\.be\/([\w\-_\?&=.]+)/i,
363+
w: 560, h: 314,
364+
url: 'https://www.youtube.com/embed/$1',
365+
},
366+
{
367+
regex: /.*youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?.*/i,
368+
w: 560, h: 314,
369+
url: 'https://www.youtube.com/embed/$2?$4',
370+
},
371+
{
372+
regex: /.*youtube.com\/embed\/([a-z0-9\?&=\-_]+).*/i,
373+
w: 560, h: 314,
374+
url: 'https://www.youtube.com/embed/$1',
375+
},
376+
];
377+
346378
const videoExtensions = ['mp4', 'mpeg', 'm4v', 'm4p', 'mov'];
347379
const audioExtensions = ['3gp', 'aac', 'flac', 'mp3', 'm4a', 'ogg', 'wav', 'webm'];
348380
const iframeExtensions = ['html', 'htm', 'php', 'asp', 'aspx', ''];
349381

350382
export function $createMediaNodeFromSrc(src: string): MediaNode {
383+
384+
for (const pattern of urlPatterns) {
385+
const match = src.match(pattern.regex);
386+
if (match) {
387+
const newSrc = src.replace(pattern.regex, pattern.url);
388+
const node = new MediaNode('iframe');
389+
node.setSrc(newSrc);
390+
node.setHeight(pattern.h);
391+
node.setWidth(pattern.w);
392+
return node;
393+
}
394+
}
395+
351396
let nodeTag: MediaNodeTag = 'iframe';
352397
const srcEnd = src.split('?')[0].split('/').pop() || '';
353398
const srcEndSplit = srcEnd.split('.');
@@ -360,7 +405,9 @@ export function $createMediaNodeFromSrc(src: string): MediaNode {
360405
nodeTag = 'embed';
361406
}
362407

363-
return new MediaNode(nodeTag);
408+
const node = new MediaNode(nodeTag);
409+
node.setSrc(src);
410+
return node;
364411
}
365412

366413
export function $isMediaNode(node: LexicalNode | null | undefined): node is MediaNode {

resources/js/wysiwyg/todo.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
## Secondary Todo
1212

13-
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
1413
- Deep check of translation coverage
1514

1615
## Bugs

resources/js/wysiwyg/ui/defaults/buttons/objects.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
} from "../../../utils/selection";
3333
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
3434
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
35-
import {$showDetailsForm, $showImageForm, $showLinkForm} from "../forms/objects";
35+
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
3636
import {formatCodeBlock} from "../../../utils/formats";
3737

3838
export const link: EditorButtonDefinition = {
@@ -168,24 +168,11 @@ export const media: EditorButtonDefinition = {
168168
label: 'Insert/edit Media',
169169
icon: mediaIcon,
170170
action(context: EditorUiContext) {
171-
const mediaModal = context.manager.createModal('media');
172-
173171
context.editor.getEditorState().read(() => {
174172
const selection = $getSelection();
175173
const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
176174

177-
let formDefaults = {};
178-
if (selectedNode) {
179-
const nodeAttrs = selectedNode.getAttributes();
180-
formDefaults = {
181-
src: nodeAttrs.src || nodeAttrs.data || '',
182-
width: nodeAttrs.width,
183-
height: nodeAttrs.height,
184-
embed: '',
185-
}
186-
}
187-
188-
mediaModal.show(formDefaults);
175+
$showMediaForm(selectedNode, context);
189176
});
190177
},
191178
isActive(selection: BaseSelection | null): boolean {

resources/js/wysiwyg/ui/defaults/forms/objects.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,23 @@ export const link: EditorFormDefinition = {
186186
],
187187
};
188188

189+
export function $showMediaForm(media: MediaNode|null, context: EditorUiContext): void {
190+
const mediaModal = context.manager.createModal('media');
191+
192+
let formDefaults = {};
193+
if (media) {
194+
const nodeAttrs = media.getAttributes();
195+
formDefaults = {
196+
src: nodeAttrs.src || nodeAttrs.data || '',
197+
width: nodeAttrs.width,
198+
height: nodeAttrs.height,
199+
embed: '',
200+
}
201+
}
202+
203+
mediaModal.show(formDefaults);
204+
}
205+
189206
export const media: EditorFormDefinition = {
190207
submitText: 'Save',
191208
async action(formData, context: EditorUiContext) {
@@ -215,12 +232,19 @@ export const media: EditorFormDefinition = {
215232
const height = (formData.get('height') || '').toString().trim();
216233
const width = (formData.get('width') || '').toString().trim();
217234

218-
const updateNode = selectedNode || $createMediaNodeFromSrc(src);
219-
updateNode.setSrc(src);
220-
updateNode.setWidthAndHeight(width, height);
221-
if (!selectedNode) {
222-
$insertNodes([updateNode]);
235+
// Update existing
236+
if (selectedNode) {
237+
selectedNode.setSrc(src);
238+
selectedNode.setWidthAndHeight(width, height);
239+
return;
240+
}
241+
242+
// Insert new
243+
const node = $createMediaNodeFromSrc(src);
244+
if (width || height) {
245+
node.setWidthAndHeight(width, height);
223246
}
247+
$insertNodes([node]);
224248
});
225249

226250
return true;

0 commit comments

Comments
 (0)