@@ -463,7 +463,16 @@ const inject = {
463463 return $target ;
464464 } ,
465465
466- _performInjection ( target , $el , $sources , cfg , trigger , $title ) {
466+ _performInjection (
467+ target ,
468+ $el ,
469+ $sources ,
470+ cfg ,
471+ trigger ,
472+ $title_tag ,
473+ $canonical_tag ,
474+ $base_tag
475+ ) {
467476 /* Called after the XHR has succeeded and we have a new $sources
468477 * element to inject.
469478 */
@@ -472,7 +481,9 @@ const inject = {
472481 const method = cfg . sourceMod === "content" ? "innerHTML" : "outerHTML" ;
473482 // There might be multiple sources, so we need to loop over them.
474483 // Access them with "innerHTML" or "outerHTML" depending on the sourceMod.
475- const sources_string = [ ...$sources ] . map ( source => source [ method ] ) . join ( "\n" ) ;
484+ const sources_string = [ ...$sources ]
485+ . map ( ( source ) => source [ method ] )
486+ . join ( "\n" ) ;
476487 wrapper . innerHTML = sources_string ;
477488
478489 for ( const img of wrapper . content . querySelectorAll ( "img" ) ) {
@@ -494,13 +505,13 @@ const inject = {
494505 // Now the injection actually happens.
495506 if ( this . _inject ( trigger , source_nodes , target , cfg ) ) {
496507 // Update history
497- this . _update_history ( cfg , trigger , $title ) ;
508+ this . _update_history ( cfg , trigger , $title_tag , $canonical_tag , $base_tag ) ;
498509 // Post-injection
499510 this . _afterInjection ( $el , cfg . $created_target || $ ( source_nodes ) , cfg ) ;
500511 }
501512 } ,
502513
503- _update_history ( cfg , trigger , $title ) {
514+ _update_history ( cfg , trigger , $title_tag , $canonical_tag , $base_tag ) {
504515 // History support. if subform is submitted, append form params
505516 if ( cfg . history !== "record" || ! history ?. pushState ) {
506517 return ;
@@ -510,16 +521,45 @@ const inject = {
510521 const glue = url . indexOf ( "?" ) > - 1 ? "&" : "?" ;
511522 url = `${ url } ${ glue } ${ cfg . params } ` ;
512523 }
513- history . pushState ( { url : url } , "" , url ) ;
524+
525+ // We have a URL changing injection. We also need to update other data:
526+ // - title
527+ // - canonical link
528+ // - base url
529+
514530 // Also inject title element if we have one
515- if ( $title ?. length ) {
531+ if ( $title_tag ?. length ) {
516532 const title_el = document . querySelector ( "title" ) ;
517533 if ( title_el ) {
518- this . _inject ( trigger , $title , title_el , {
534+ this . _inject ( trigger , $title_tag , title_el , {
535+ action : "element" ,
536+ } ) ;
537+ }
538+ }
539+
540+ // Also inject canonical element if we have one
541+ if ( $canonical_tag ?. length ) {
542+ const canonical_el = document . querySelector ( "link[rel=canonical]" ) ;
543+ if ( canonical_el ) {
544+ this . _inject ( trigger , $canonical_tag , canonical_el , {
519545 action : "element" ,
520546 } ) ;
521547 }
522548 }
549+
550+ // Also inject base element if we have one
551+ if ( $base_tag ?. length ) {
552+ const base_el = document . querySelector ( "base" ) ;
553+ if ( base_el ) {
554+ this . _inject ( trigger , $base_tag , base_el , {
555+ action : "element" ,
556+ } ) ;
557+ }
558+ }
559+
560+ // At last position - other patterns can react on already changed title,
561+ // canonical or base.
562+ history . pushState ( { url : url } , "" , url ) ;
523563 } ,
524564
525565 _afterInjection ( $el , $injected , cfg ) {
@@ -592,16 +632,21 @@ const inject = {
592632 data ,
593633 ev ,
594634 ] ) ;
595- /* pick the title source for dedicated handling later
596- Title - if present - is always appended at the end. */
597- let $title ;
598- if (
599- sources$ &&
600- sources$ [ sources$ . length - 1 ] &&
601- sources$ [ sources$ . length - 1 ] [ 0 ] &&
602- sources$ [ sources$ . length - 1 ] [ 0 ] . nodeName === "TITLE"
603- ) {
604- $title = sources$ [ sources$ . length - 1 ] ;
635+
636+ const extra_sources = sources$ . filter ( ( source ) => source instanceof Array ) ;
637+ let $title_tag ;
638+ let $canonical_tag ;
639+ let $base_tag ;
640+ for ( const extra_source of extra_sources ) {
641+ if ( extra_source [ 0 ] === "head_title" ) {
642+ $title_tag = extra_source [ 1 ] ;
643+ }
644+ if ( extra_source [ 0 ] === "head_canonical" ) {
645+ $canonical_tag = extra_source [ 1 ] ;
646+ }
647+ if ( extra_source [ 0 ] === "head_base" ) {
648+ $base_tag = extra_source [ 1 ] ;
649+ }
605650 }
606651
607652 for ( const [ idx1 , cfg ] of cfgs . entries ( ) ) {
@@ -614,7 +659,9 @@ const inject = {
614659 sources$ [ idx1 ] ,
615660 cfg ,
616661 ev . target ,
617- $title
662+ $title_tag ,
663+ $canonical_tag ,
664+ $base_tag
618665 ) ;
619666 }
620667 }
@@ -834,19 +881,33 @@ const inject = {
834881
835882 _sourcesFromHtml ( html , url , sources ) {
836883 const $html = this . _parseRawHtml ( html , url ) ;
837- return sources . map ( ( source ) => {
884+ const $sources = sources . map ( ( source ) => {
885+ // Special cases for automatic title and canonical manipulation for
886+ // history:records injections.
887+
888+ if ( source === "head_title" ) {
889+ return [ "head_title" , $html . find ( "title" ) ] ;
890+ }
891+ if ( source === "head_canonical" ) {
892+ return [ "head_canonical" , $html . find ( "link[rel=canonical]" ) ] ;
893+ }
894+ if ( source === "head_base" ) {
895+ return [ "head_base" , $html . find ( "base" ) ] ;
896+ }
897+
898+ // Special case for body
838899 if ( source === "body" ) {
839900 source = "#__original_body" ;
840901 }
902+
903+ // Special case for "none";
841904 if ( source === "none" ) {
842905 return $ ( "<!-- -->" ) ;
843906 }
844907 const $source = $html . find ( source ) ;
845908
846909 if ( $source . length === 0 ) {
847- if ( source != "title" ) {
848- log . warn ( "No source elements for selector:" , source , $html ) ;
849- }
910+ log . warn ( "No source elements for selector:" , source , $html ) ;
850911 }
851912
852913 $source . find ( 'a[href^="#"]' ) . each ( ( idx , el_ ) => {
@@ -868,6 +929,7 @@ const inject = {
868929 } ) ;
869930 return $source ;
870931 } ) ;
932+ return $sources ;
871933 } ,
872934
873935 _rebaseAttrs : {
@@ -973,7 +1035,11 @@ const inject = {
9731035
9741036 _parseRawHtml ( html , url = "" ) {
9751037 // remove script tags and head and replace body by a div
976- const title = html . match ( / \< t i t l e \> ( .* ) \< \/ t i t l e \> / ) ;
1038+ const title_tag = html . match ( / \< t i t l e \> ( .* ) \< \/ t i t l e \> / ) ;
1039+ const canonical_tag = html . match (
1040+ / < l i n k \b [ ^ > ] * \b r e l \s * = \s * [ " ' ] c a n o n i c a l [ " ' ] [ ^ > ] * > / i
1041+ ) ;
1042+ const base_tag = html . match ( / < b a s e \b [ ^ > ] * > / i) ;
9771043 let clean_html = html
9781044 . replace ( / < s c r i p t \b [ ^ < ] * (?: (? ! < \/ s c r i p t > ) < [ ^ < ] * ) * < \/ s c r i p t > / gi, "" )
9791045 . replace ( / < h e a d \b [ ^ < ] * (?: (? ! < \/ s c r i p t > ) < [ ^ < ] * ) * < \/ h e a d > / gi, "" )
@@ -982,8 +1048,14 @@ const inject = {
9821048 . replace ( / < b o d y ( [ ^ > ] * ?) > / gi, '<div id="__original_body">' )
9831049 . replace ( / < \/ b o d y ( [ ^ > ] * ?) > / gi, "</div>" ) ;
9841050
985- if ( title && title . length == 2 ) {
986- clean_html = title [ 0 ] + clean_html ;
1051+ if ( title_tag && title_tag . length == 2 ) {
1052+ clean_html = title_tag [ 0 ] + clean_html ;
1053+ }
1054+ if ( canonical_tag ) {
1055+ clean_html = canonical_tag [ 0 ] + clean_html ;
1056+ }
1057+ if ( base_tag ) {
1058+ clean_html = base_tag [ 0 ] + clean_html ;
9871059 }
9881060 try {
9891061 clean_html = this . _rebaseHTML ( url , clean_html ) ;
@@ -1121,7 +1193,9 @@ const inject = {
11211193 html : {
11221194 sources ( cfgs , data ) {
11231195 const sources = cfgs . map ( ( cfg ) => cfg . source ) ;
1124- sources . push ( "title" ) ;
1196+ sources . push ( "head_title" ) ;
1197+ sources . push ( "head_canonical" ) ;
1198+ sources . push ( "head_base" ) ;
11251199 const result = this . _sourcesFromHtml ( data , cfgs [ 0 ] . url , sources ) ;
11261200 return result ;
11271201 } ,
0 commit comments