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.
} from "lexical/nodes/common";
import {$selectSingleNode} from "../../utils/selection";
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
+import * as url from "node:url";
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
export type MediaNodeSource = {
return domElementToNode(tag as MediaNodeTag, el);
}
+interface UrlPattern {
+ readonly regex: RegExp;
+ readonly w: number;
+ readonly h: number;
+ readonly url: string;
+}
+
+/**
+ * These patterns originate from the tinymce/tinymce project.
+ * https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts
+ * License: MIT Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
+ * License Link: https://github.com/tinymce/tinymce/blob/584a150679669859a528828e5d2910a083b1d911/LICENSE.TXT
+ */
+const urlPatterns: UrlPattern[] = [
+ {
+ regex: /.*?youtu\.be\/([\w\-_\?&=.]+)/i,
+ w: 560, h: 314,
+ url: 'https://www.youtube.com/embed/$1',
+ },
+ {
+ regex: /.*youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?.*/i,
+ w: 560, h: 314,
+ url: 'https://www.youtube.com/embed/$2?$4',
+ },
+ {
+ regex: /.*youtube.com\/embed\/([a-z0-9\?&=\-_]+).*/i,
+ w: 560, h: 314,
+ url: 'https://www.youtube.com/embed/$1',
+ },
+];
+
const videoExtensions = ['mp4', 'mpeg', 'm4v', 'm4p', 'mov'];
const audioExtensions = ['3gp', 'aac', 'flac', 'mp3', 'm4a', 'ogg', 'wav', 'webm'];
const iframeExtensions = ['html', 'htm', 'php', 'asp', 'aspx', ''];
export function $createMediaNodeFromSrc(src: string): MediaNode {
+
+ for (const pattern of urlPatterns) {
+ const match = src.match(pattern.regex);
+ if (match) {
+ const newSrc = src.replace(pattern.regex, pattern.url);
+ const node = new MediaNode('iframe');
+ node.setSrc(newSrc);
+ node.setHeight(pattern.h);
+ node.setWidth(pattern.w);
+ return node;
+ }
+ }
+
let nodeTag: MediaNodeTag = 'iframe';
const srcEnd = src.split('?')[0].split('/').pop() || '';
const srcEndSplit = srcEnd.split('.');
nodeTag = 'embed';
}
- return new MediaNode(nodeTag);
+ const node = new MediaNode(nodeTag);
+ node.setSrc(src);
+ return node;
}
export function $isMediaNode(node: LexicalNode | null | undefined): node is MediaNode {
## Secondary Todo
-- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
- Deep check of translation coverage
## Bugs
} from "../../../utils/selection";
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
-import {$showDetailsForm, $showImageForm, $showLinkForm} from "../forms/objects";
+import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
import {formatCodeBlock} from "../../../utils/formats";
export const link: EditorButtonDefinition = {
label: 'Insert/edit Media',
icon: mediaIcon,
action(context: EditorUiContext) {
- const mediaModal = context.manager.createModal('media');
-
context.editor.getEditorState().read(() => {
const selection = $getSelection();
const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
- let formDefaults = {};
- if (selectedNode) {
- const nodeAttrs = selectedNode.getAttributes();
- formDefaults = {
- src: nodeAttrs.src || nodeAttrs.data || '',
- width: nodeAttrs.width,
- height: nodeAttrs.height,
- embed: '',
- }
- }
-
- mediaModal.show(formDefaults);
+ $showMediaForm(selectedNode, context);
});
},
isActive(selection: BaseSelection | null): boolean {
],
};
+export function $showMediaForm(media: MediaNode|null, context: EditorUiContext): void {
+ const mediaModal = context.manager.createModal('media');
+
+ let formDefaults = {};
+ if (media) {
+ const nodeAttrs = media.getAttributes();
+ formDefaults = {
+ src: nodeAttrs.src || nodeAttrs.data || '',
+ width: nodeAttrs.width,
+ height: nodeAttrs.height,
+ embed: '',
+ }
+ }
+
+ mediaModal.show(formDefaults);
+}
+
export const media: EditorFormDefinition = {
submitText: 'Save',
async action(formData, context: EditorUiContext) {
const height = (formData.get('height') || '').toString().trim();
const width = (formData.get('width') || '').toString().trim();
- const updateNode = selectedNode || $createMediaNodeFromSrc(src);
- updateNode.setSrc(src);
- updateNode.setWidthAndHeight(width, height);
- if (!selectedNode) {
- $insertNodes([updateNode]);
+ // Update existing
+ if (selectedNode) {
+ selectedNode.setSrc(src);
+ selectedNode.setWidthAndHeight(width, height);
+ return;
+ }
+
+ // Insert new
+ const node = $createMediaNodeFromSrc(src);
+ if (width || height) {
+ node.setWidthAndHeight(width, height);
}
+ $insertNodes([node]);
});
return true;