import { Injectable , Optional , Inject , SecurityContext } from '@angular/core' ; import { Title , Meta , DomSanitizer } from '@angular/platform-browser' ; import { SiteService } from './site.service' ; import { Location } from '@angular/common' ; import { ConfigsService } from './configs.service' ; import { DOCUMENT } from '@angular/common' ; const DEFAULT_META_TITLE = 'Minds' ; const DEFAULT_META_DESCRIPTION = '...' ; export const MIN_METRIC_FOR_ROBOTS = 5 ; const DEFAULT_META_AUTHOR = 'Minds' ; const DEFAULT_OG_IMAGE = '/assets/og-images/default-v3.png' ; const DEFAULT_OG_IMAGE_WIDTH = 1200 ; const DEFAULT_OG_IMAGE_HEIGHT = 1200 ; @ Injectable ( { providedIn : 'root' , } ) export class MetaService { private counter : number ; private sep = ' | ' ; private title : string = '' ; private ogTitle : string = '' ; constructor ( private titleService , private metaService , private site , private location , private configs , @ Inject ( DOCUMENT ) private dom , private domSanitizer ) { this . reset ( ) ; } setTitle ( value , join = true ) { let title ; const defaultTitle = this . site . isProDomain ? this . site . title + ' - ' + this . site . oneLineHeadline : DEFAULT_META_TITLE ; value = this . stripHtml ( value ) ; this . ogTitle = value || defaultTitle ; if ( value . length > 60 ) { value = value . substr ( 0 , 57 ) + '...' ; } if ( value && join ) { title = [ value , defaultTitle ] . filter ( fragment => Boolean ( fragment ) ) . join ( this . sep ) ; } else if ( value ) { title = value ; } else { title = defaultTitle ; } this . title = title ; this . applyTitle ( ) ; return this ; } setDescription ( value ) { value = this . stripHtml ( value ) ; if ( value . length > 160 ) { value = value . substr ( 0 , 157 ) + '...' ; } this . metaService . updateTag ( { name : 'description' , content : value } ) ; this . metaService . updateTag ( { property : 'og:description' , content : value } ) ; return this ; } setCounter ( value ) { this . counter = value ; this . applyTitle ( ) ; return this ; } setCanonicalUrl ( value ) { const links = this . dom . head . querySelectorAll ( '[rel="canonical"]' ) ; if ( links . length ) { for ( const link of links ) { this . dom . head . removeChild ( link ) ; } } if ( value ) { if ( value && value . indexOf ( '/' ) === 0 ) { value = this . site . baseUrl + value . substr ( 1 ) ; } let link ; link = this . dom . createElement ( 'link' ) ; link . setAttribute ( 'rel' , 'canonical' ) ; link . setAttribute ( 'href' , value ) ; this . dom . head . appendChild ( link ) ; } return this ; } /**
     * Used for pro domains
     */ setDynamicFavicon ( href ) { const existingLink = this . dom . head . querySelector ( '#dynamicFavicon' ) ; if ( existingLink ) { existingLink . setAttribute ( 'href' , href ) ; } else { let link ; link = this . dom . createElement ( 'link' ) ; link . setAttribute ( 'rel' , 'icon' ) ; link . setAttribute ( 'type' , 'image/png' ) ; link . setAttribute ( 'href' , href ) ; link . setAttribute ( 'id' , 'dynamicFavicon' ) ; this . dom . head . appendChild ( link ) ; } return this ; } resetDynamicFavicon ( ) { const link = this . dom . head . querySelector ( '#dynamicFavicon' ) ; if ( link ) { this . dom . head . removeChild ( link ) ; } return this ; } setOgUrl ( value ) { if ( value && value . indexOf ( '/' ) === 0 ) { value = this . site . baseUrl + value . substr ( 1 ) ; } this . metaService . updateTag ( { property : 'og:url' , content : value , } ) ; return this ; } setOgImage ( value , @ Optional ( ) dimensions = null ) { if ( value ) { if ( value . indexOf ( '/' ) === 0 ) { value = this . configs . get ( 'cdn_assets_url' ) + value . substr ( 1 ) ; } this . metaService . updateTag ( { property : 'og:image' , content : value } ) ; if ( dimensions ) { this . metaService . updateTag ( { property : 'og:image:width' , content : dimensions . width . toString ( ) , } ) ; this . metaService . updateTag ( { property : 'og:image:height' , content : dimensions . height . toString ( ) , } ) ; } } return this ; } setOgType ( value ) { this . metaService . updateTag ( { property : 'og:type' , content : value , } ) ; return this ; } setLanguage ( language ) { return this ; } setRobots ( value ) { this . metaService . updateTag ( { name : 'robots' , content : value } ) ; return this ; } setNsfw ( value ) { if ( value ) { this . metaService . updateTag ( { name : 'rating' , content : 'adult' } ) ; } else { this . metaService . removeTag ( "name='rating'" ) ; } return this ; } param { entityGuid } returns { MetaService } public setOEmbed ( entityGuid ) { const existingLink = this . dom . head . querySelector ( '#oEmbed' ) ; if ( existingLink ) { existingLink . setAttribute ( 'href' , this . getOEmbedUrl ( entityGuid ) ) ; } else { let link ; link = this . dom . createElement ( 'link' ) ; link . setAttribute ( 'rel' , 'alternative' ) ; link . setAttribute ( 'href' , this . getOEmbedUrl ( entityGuid ) ) ; link . setAttribute ( 'type' , 'application/json+oembed' ) ; link . setAttribute ( 'id' , 'oEmbed' ) ; this . dom . head . appendChild ( link ) ; } return this ; } setAuthor ( value ) { this . metaService . updateTag ( { property : 'author' , content : value , } ) ; return this ; } setOgAuthor ( value ) { this . metaService . updateTag ( { property : 'og:author' , content : value , } ) ; return this ; } setOgSiteName ( ) { this . metaService . updateTag ( { property : 'og:site_name' , content : this . site . title , } ) ; return this ; } reset ( data = { } ) { this . setTitle ( data . title || '' ) . setDescription ( data . description || DEFAULT_META_DESCRIPTION ) . setOgType ( 'website' ) . setOgUrl ( data . ogUrl || this . location . path ( ) ) . setOgImage ( data . ogImage || DEFAULT_OG_IMAGE , data . ogImage ? data . ogImageWidth && data . ogImageHeight ? { width : data . ogImageWidth , height : data . ogImageHeight , } : null : { width : DEFAULT_OG_IMAGE_WIDTH , height : DEFAULT_OG_IMAGE_HEIGHT } ) . setAuthor ( data . author || DEFAULT_META_AUTHOR ) . setOgAuthor ( data . ogAuthor || DEFAULT_META_AUTHOR ) . setCanonicalUrl ( data . canonicalUrl || '' ) . setRobots ( data . robots || 'all' ) . setNsfw ( false ) . setOgSiteName ( ) . resetDynamicFavicon ( ) . resetOEmbed ( ) ; } private applyTitle ( ) { if ( this . counter ) { this . titleService . setTitle ( `(*) ${ this . title }` ) ; } else { this . titleService . setTitle ( this . title ) ; } this . metaService . updateTag ( { property : 'og:title' , content : this . ogTitle , } ) ; } param value return private stripHtml ( value ) { if ( ! value ) return '' ; const fakeEl = this . dom . createElement ( 'span' ) ; fakeEl . innerHTML = this . domSanitizer . sanitize ( SecurityContext . HTML , value ) ; return fakeEl . textContent || fakeEl . innerText ; } param { entityGuid } returns { string } private getOEmbedUrl ( entityGuid ) { const baseUrl = this . site . baseUrl ; const encodedOEmbedUrl = encodeURIComponent ( `${ baseUrl }newsfeed/${ entityGuid }` ) ; return `${ baseUrl }api/v3/oembed\?url=${ encodedOEmbedUrl }` ; } param { MetaService } private resetOEmbed ( ) { const link = this . dom . head . querySelector ( '#oEmbed' ) ; if ( link ) { this . dom . head . removeChild ( link ) ; } return this ; } }