@ngx-meta/core
Version:
Dynamic page title & meta tags utility for Angular (w/server-side rendering)
274 lines (266 loc) • 11.1 kB
JavaScript
import { ɵɵinject, ɵɵdefineInjectable, ɵsetClassMetadata, Injectable, ɵɵdefineNgModule, ɵɵdefineInjector, NgModule, Optional, SkipSelf } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';
import { of, from } from 'rxjs';
var PageTitlePositioning;
(function (PageTitlePositioning) {
PageTitlePositioning[PageTitlePositioning["AppendPageTitle"] = 0] = "AppendPageTitle";
PageTitlePositioning[PageTitlePositioning["PrependPageTitle"] = 10] = "PrependPageTitle";
})(PageTitlePositioning || (PageTitlePositioning = {}));
class MetaLoader {
}
class MetaStaticLoader {
constructor(providedSettings = {
pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
defaults: {}
}) {
this.providedSettings = providedSettings;
}
get settings() {
return this.providedSettings;
}
}
const isPromise = (obj) => !!obj && typeof obj.then === 'function';
const isObservable = (obj) => !!obj && typeof obj.subscribe === 'function';
class MetaService {
constructor(loader, title, meta) {
this.loader = loader;
this.title = title;
this.meta = meta;
this.settings = loader.settings;
this.isMetaTagSet = {};
}
setTitle(title, override = false) {
const title$ = title ? this.callback(title) : of('');
title$.subscribe((res) => {
let fullTitle = '';
if (!res) {
const defaultTitle$ = this.settings.defaults && this.settings.defaults.title ? this.callback(this.settings.defaults.title) : of('');
defaultTitle$.subscribe((defaultTitle) => {
if (!override && this.settings.pageTitleSeparator && this.settings.applicationName) {
this.callback(this.settings.applicationName).subscribe((applicationName) => {
fullTitle = applicationName ? this.getTitleWithPositioning(defaultTitle, applicationName) : defaultTitle;
this.updateTitle(fullTitle);
});
}
else {
this.updateTitle(defaultTitle);
}
});
}
else if (!override && this.settings.pageTitleSeparator && this.settings.applicationName) {
this.callback(this.settings.applicationName).subscribe((applicationName) => {
fullTitle = applicationName ? this.getTitleWithPositioning(res, applicationName) : res;
this.updateTitle(fullTitle);
});
}
else {
this.updateTitle(res);
}
});
}
setTag(key, value) {
if (key === 'title') {
throw new Error(`Attempt to set ${key} through "setTag": "title" is a reserved tag name. ` + 'Please use `MetaService.setTitle` instead.');
}
const cur = value || (this.settings.defaults && this.settings.defaults[key] ? this.settings.defaults[key] : '');
const value$ = key !== 'og:locale' && key !== 'og:locale:alternate' ? this.callback(cur) : of(cur);
value$.subscribe((res) => {
this.updateTag(key, res);
});
}
update(currentUrl, metaSettings) {
if (!metaSettings) {
const fallbackTitle = this.settings.defaults
? this.settings.defaults.title || this.settings.applicationName
: this.settings.applicationName;
this.setTitle(fallbackTitle, true);
}
else {
if (metaSettings.disabled) {
this.update(currentUrl);
return;
}
this.setTitle(metaSettings.title, metaSettings.override);
Object.keys(metaSettings).forEach(key => {
let value = metaSettings[key];
if (key === 'title' || key === 'override') {
return;
}
else if (key === 'og:locale') {
value = value.replace(/-/g, '_');
}
else if (key === 'og:locale:alternate') {
const currentLocale = metaSettings['og:locale'];
this.updateLocales(currentLocale, metaSettings[key]);
return;
}
this.setTag(key, value);
});
}
if (this.settings.defaults) {
Object.keys(this.settings.defaults).forEach(key => {
let value = this.settings.defaults[key];
if ((metaSettings && (key in this.isMetaTagSet || key in metaSettings)) || key === 'title' || key === 'override') {
return;
}
else if (key === 'og:locale') {
value = value.replace(/-/g, '_');
}
else if (key === 'og:locale:alternate') {
const currentLocale = metaSettings ? metaSettings['og:locale'] : undefined;
this.updateLocales(currentLocale, value);
return;
}
this.setTag(key, value);
});
}
const baseUrl = this.settings.applicationUrl ? this.settings.applicationUrl : '/';
const url = `${baseUrl}${currentUrl}`.replace(/(https?:\/\/)|(\/)+/g, '$1$2').replace(/\/$/g, '');
this.setTag('og:url', url ? url : '/');
}
removeTag(key) {
this.meta.removeTag(key);
}
callback(value) {
if (this.settings.callback) {
const value$ = this.settings.callback(value);
if (!isObservable(value$)) {
return isPromise(value$) ? from(value$) : of(value$);
}
return value$;
}
return of(value);
}
getTitleWithPositioning(title, applicationName) {
switch (this.settings.pageTitlePositioning) {
case PageTitlePositioning.AppendPageTitle:
return applicationName + String(this.settings.pageTitleSeparator) + title;
case PageTitlePositioning.PrependPageTitle:
return title + String(this.settings.pageTitleSeparator) + applicationName;
default:
throw new Error(`Invalid pageTitlePositioning specified [${this.settings.pageTitlePositioning}]!`);
}
}
updateTitle(title) {
this.title.setTitle(title);
this.meta.updateTag({
property: 'og:title',
content: title
});
}
updateLocales(currentLocale, availableLocales) {
const cur = currentLocale || (this.settings.defaults ? this.settings.defaults['og:locale'] : '');
if (cur && this.settings.defaults) {
this.settings.defaults['og:locale'] = cur.replace(/_/g, '-');
}
const elements = this.meta.getTags('property="og:locale:alternate"');
elements.forEach((element) => {
this.meta.removeTagElement(element);
});
if (cur && availableLocales) {
availableLocales.split(',').forEach((locale) => {
if (cur.replace(/-/g, '_') !== locale.replace(/-/g, '_')) {
this.meta.addTag({
property: 'og:locale:alternate',
content: locale.replace(/-/g, '_')
});
}
});
}
}
updateTag(key, value) {
if (key.lastIndexOf('og:', 0) === 0) {
this.meta.updateTag({
property: key,
content: key === 'og:locale' ? value.replace(/-/g, '_') : value
});
}
else {
this.meta.updateTag({
name: key,
content: value
});
}
this.isMetaTagSet[key] = true;
if (key === 'description') {
this.meta.updateTag({
property: 'og:description',
content: value
});
}
else if (key === 'author') {
this.meta.updateTag({
property: 'og:author',
content: value
});
}
else if (key === 'publisher') {
this.meta.updateTag({
property: 'og:publisher',
content: value
});
}
else if (key === 'og:locale') {
const availableLocales = this.settings.defaults ? this.settings.defaults['og:locale:alternate'] : '';
this.updateLocales(value, availableLocales);
this.isMetaTagSet['og:locale:alternate'] = true;
}
else if (key === 'og:locale:alternate') {
const currentLocale = this.meta.getTag('property="og:locale"').content;
this.updateLocales(currentLocale, value);
this.isMetaTagSet['og:locale'] = true;
}
}
}
MetaService.ɵfac = function MetaService_Factory(t) { return new (t || MetaService)(ɵɵinject(MetaLoader), ɵɵinject(Title), ɵɵinject(Meta)); };
MetaService.ɵprov = ɵɵdefineInjectable({ token: MetaService, factory: MetaService.ɵfac });
(function () { ɵsetClassMetadata(MetaService, [{
type: Injectable
}], function () { return [{ type: MetaLoader }, { type: Title }, { type: Meta }]; }, null); })();
class MetaGuard {
constructor(meta) {
this.meta = meta;
}
canActivate(route, state) {
const url = state.url;
const metaSettings = route.hasOwnProperty('data') ? route.data.meta : undefined;
this.meta.update(url, metaSettings);
return true;
}
canActivateChild(route, state) {
return this.canActivate(route, state);
}
}
MetaGuard.ɵfac = function MetaGuard_Factory(t) { return new (t || MetaGuard)(ɵɵinject(MetaService)); };
MetaGuard.ɵprov = ɵɵdefineInjectable({ token: MetaGuard, factory: MetaGuard.ɵfac });
(function () { ɵsetClassMetadata(MetaGuard, [{
type: Injectable
}], function () { return [{ type: MetaService }]; }, null); })();
const metaFactory = () => new MetaStaticLoader();
class MetaModule {
constructor(parentModule) {
if (parentModule) {
throw new Error('MetaModule already loaded; import in root module only.');
}
}
static forRoot(configuredProvider = {
provide: MetaLoader,
useFactory: metaFactory
}) {
return {
ngModule: MetaModule,
providers: [configuredProvider, MetaGuard, MetaService]
};
}
}
MetaModule.ɵmod = ɵɵdefineNgModule({ type: MetaModule });
MetaModule.ɵinj = ɵɵdefineInjector({ factory: function MetaModule_Factory(t) { return new (t || MetaModule)(ɵɵinject(MetaModule, 12)); } });
(function () { ɵsetClassMetadata(MetaModule, [{
type: NgModule
}], function () { return [{ type: MetaModule, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }]; }, null); })();
export { MetaGuard, MetaLoader, MetaModule, MetaService, MetaStaticLoader, PageTitlePositioning, metaFactory };
//# sourceMappingURL=ngx-meta-core.js.map