Project

General

Profile

1
/**
2
 * @license
3
 * Copyright Google Inc. All Rights Reserved.
4
 *
5
 * Use of this source code is governed by an MIT-style license that can be
6
 * found in the LICENSE file at https://angular.io/license
7
 */
8

    
9
import {Injectable} from '@angular/core';
10
// es6-modules are used here
11
import {DomAdapter, getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
12

    
13
/**
14
 * Represent meta element.
15
 *
16
 * ### Example
17
 *
18
 * ```ts
19
 * { name: 'application-name', content: 'Name of my application' },
20
 * { name: 'description', content: 'A description of the page', id: 'desc' }
21
 * // ...
22
 * // Twitter
23
 * { name: 'twitter:title', content: 'Content Title' }
24
 * // ...
25
 * // Google+
26
 * { itemprop: 'name', content: 'Content Title' },
27
 * { itemprop: 'description', content: 'Content Title' }
28
 * // ...
29
 * // Facebook / Open Graph
30
 * { property: 'fb:app_id', content: '123456789' },
31
 * { property: 'og:title', content: 'Content Title' }
32
 * ```
33
 *
34
 * @experimental
35
 */
36
export interface MetaDefinition {
37
  charset?: string;
38
  content?: string;
39
  httpEquiv?: string;
40
  id?: string;
41
  itemprop?: string;
42
  name?: string;
43
  property?: string;
44
  scheme?: string;
45
  url?: string;
46
  [prop: string]: string;
47
}
48

    
49
/**
50
 * A service that can be used to get and add meta tags.
51
 *
52
 * @experimental
53
 */
54
@Injectable()
55
export class Meta {
56
  private _dom: DomAdapter = getDOM();
57

    
58
  /**
59
   * Adds a new meta tag to the dom.
60
   *
61
   *  ### Example
62
   *
63
   * ```ts
64
   * const name: MetaDefinition = {name: 'application-name', content: 'Name of my application'};
65
   * const desc: MetaDefinition = {name: 'description', content: 'A description of the page'};
66
   * const tags: HTMLMetaElement[] = this.meta.addTags([name, desc]);
67
   * ```
68
   *
69
   * @param tags
70
   * @returns {HTMLMetaElement[]}
71
   */
72
  addTags(...tags: Array<MetaDefinition|MetaDefinition[]>): HTMLMetaElement[] {
73
    const presentTags = this._flattenArray(tags);
74
    if (presentTags.length === 0) return [];
75
    return presentTags.map((tag: MetaDefinition) => this._addInternal(tag));
76
  }
77

    
78
  /**
79
   * Gets the meta tag by the given selector. Returns element or null
80
   * if there's no such meta element.
81
   *
82
   *  ### Example
83
   *
84
   * ```ts
85
   * const meta: HTMLMetaElement = this.meta.getTag('name=description');
86
   * const twitterMeta: HTMLMetaElement = this.meta.getTag('name="twitter:title"');
87
   * const fbMeta: HTMLMetaElement = this.meta.getTag('property="fb:app_id"');
88
   * ```
89
   *
90
   * @param selector
91
   * @returns {HTMLMetaElement}
92
   */
93
  getTag(selector: string): HTMLMetaElement {
94
    if (!selector) return null;
95
    return this._dom.query(`meta[${selector}]`);
96
  }
97

    
98
  /**
99
   * Updates the meta tag with the given selector.
100
   *
101
   * *  ### Example
102
   *
103
   * ```ts
104
   * const meta: HTMLMetaElement = this.meta.updateTag('name=description', {name: 'description',
105
   * content: 'New description'});
106
   * console.log(meta.content); // 'New description'
107
   * ```
108
   *
109
   * @param selector
110
   * @param tag updated tag definition
111
   * @returns {HTMLMetaElement}
112
   */
113
  updateTag(selector: string, tag: MetaDefinition): HTMLMetaElement {
114
    const meta: HTMLMetaElement = this.getTag(selector);
115
    if (!meta) {
116
      // create element if it doesn't exist
117
      return this._addInternal(tag);
118
    }
119
    return this._prepareMetaElement(tag, meta);
120
  }
121

    
122
  /**
123
   * Removes meta tag with the given selector from the dom.
124
   *
125
   *  ### Example
126
   *
127
   * ```ts
128
   * this.meta.removeTagBySelector('name=description');
129
   * ```
130
   *
131
   * @param selector
132
   */
133
  removeTagBySelector(selector: string): void {
134
    const meta: HTMLMetaElement = this.getTag(selector);
135
    this.removeTagElement(meta);
136
  }
137

    
138
  /**
139
   * Removes given meta element from the dom.
140
   *
141
   *  ### Example
142
   *  ```ts
143
   * const elem: HTMLMetaElement = this.meta.getTag('name=description');
144
   * this.meta.removeTagElement(elem);
145
   * ```
146
   *
147
   * @param meta meta element
148
   */
149
  removeTagElement(meta: HTMLMetaElement): void {
150
    if (meta) {
151
      this._removeMetaElement(meta);
152
    }
153
  }
154

    
155
  private _addInternal(tag: MetaDefinition): HTMLMetaElement {
156
    const meta: HTMLMetaElement = this._createMetaElement();
157
    this._prepareMetaElement(tag, meta);
158
    this._appendMetaElement(meta);
159
    return meta;
160
  }
161

    
162
  private _createMetaElement(): HTMLMetaElement {
163
    return this._dom.createElement('meta') as HTMLMetaElement;
164
  }
165

    
166
  private _prepareMetaElement(tag: MetaDefinition, el: HTMLMetaElement): HTMLMetaElement {
167
    Object.keys(tag).forEach((prop: string) => this._dom.setAttribute(el, prop, tag[prop]));
168
    return el;
169
  }
170

    
171
  private _appendMetaElement(meta: HTMLMetaElement): void {
172
    const head = this._dom.getElementsByTagName(this._dom.defaultDoc(), 'head')[0];
173
    this._dom.appendChild(head, meta);
174
  }
175

    
176
  private _removeMetaElement(meta: HTMLMetaElement): void {
177
    const head = this._dom.parentElement(meta);
178
    this._dom.removeChild(head, meta);
179
  }
180

    
181
  private _flattenArray(input: any[], out: any[] = []): any[] {
182
    if (input) {
183
      for (let i = 0; i < input.length; i++) {
184
        const item: any = input[i];
185
        if (Array.isArray(item)) {
186
          this._flattenArray(item, out);
187
        } else if (item) {
188
          out.push(item);
189
        }
190
      }
191
    }
192
    return out;
193
  }
194
}
(3-3/12)