@@ -16,6 +16,7 @@ import {
16
16
} from "lexical/nodes/common" ;
17
17
import { $selectSingleNode } from "../../utils/selection" ;
18
18
import { SerializedCommonBlockNode } from "lexical/nodes/CommonBlockNode" ;
19
+ import * as url from "node:url" ;
19
20
20
21
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio' ;
21
22
export type MediaNodeSource = {
@@ -343,11 +344,55 @@ export function $createMediaNodeFromHtml(html: string): MediaNode | null {
343
344
return domElementToNode ( tag as MediaNodeTag , el ) ;
344
345
}
345
346
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 : / .* ?y o u t u \. b e \/ ( [ \w \- _ \? & = . ] + ) / i,
363
+ w : 560 , h : 314 ,
364
+ url : 'https://www.youtube.com/embed/$1' ,
365
+ } ,
366
+ {
367
+ regex : / .* y o u t u b e \. c o m ( .+ ) v = ( [ ^ & ] + ) ( & ( [ a - z 0 - 9 & = \- _ ] + ) ) ? .* / i,
368
+ w : 560 , h : 314 ,
369
+ url : 'https://www.youtube.com/embed/$2?$4' ,
370
+ } ,
371
+ {
372
+ regex : / .* y o u t u b e .c o m \/ e m b e d \/ ( [ a - z 0 - 9 \? & = \- _ ] + ) .* / i,
373
+ w : 560 , h : 314 ,
374
+ url : 'https://www.youtube.com/embed/$1' ,
375
+ } ,
376
+ ] ;
377
+
346
378
const videoExtensions = [ 'mp4' , 'mpeg' , 'm4v' , 'm4p' , 'mov' ] ;
347
379
const audioExtensions = [ '3gp' , 'aac' , 'flac' , 'mp3' , 'm4a' , 'ogg' , 'wav' , 'webm' ] ;
348
380
const iframeExtensions = [ 'html' , 'htm' , 'php' , 'asp' , 'aspx' , '' ] ;
349
381
350
382
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
+
351
396
let nodeTag : MediaNodeTag = 'iframe' ;
352
397
const srcEnd = src . split ( '?' ) [ 0 ] . split ( '/' ) . pop ( ) || '' ;
353
398
const srcEndSplit = srcEnd . split ( '.' ) ;
@@ -360,7 +405,9 @@ export function $createMediaNodeFromSrc(src: string): MediaNode {
360
405
nodeTag = 'embed' ;
361
406
}
362
407
363
- return new MediaNode ( nodeTag ) ;
408
+ const node = new MediaNode ( nodeTag ) ;
409
+ node . setSrc ( src ) ;
410
+ return node ;
364
411
}
365
412
366
413
export function $isMediaNode ( node : LexicalNode | null | undefined ) : node is MediaNode {
0 commit comments