spotify-service-rg
Version:
This is a Angular service for connecting to and using Spotify API.
635 lines • 47.2 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
var SpotifyRgService = /** @class */ (function () {
function SpotifyRgService(http) {
this.http = http;
this.baseUrl = "https://api.spotify.com/v1";
}
/**
* @return {?}
*/
SpotifyRgService.prototype.login = /**
* @return {?}
*/
function () {
window.location.href = 'https://accounts.spotify.com/authorize' +
'?response_type=token' +
'&client_id=' + this.clientId +
(this.scopes ? '&scope=' + encodeURIComponent(this.scopes) : '') +
'&redirect_uri=' + encodeURIComponent(this.callBackUrl);
};
//Setters for the configuration properties
//Setters for the configuration properties
/**
* @param {?} clientId
* @return {?}
*/
SpotifyRgService.prototype.setClientId =
//Setters for the configuration properties
/**
* @param {?} clientId
* @return {?}
*/
function (clientId) {
this.clientId = clientId;
};
/**
* @param {?} callBackUrl
* @return {?}
*/
SpotifyRgService.prototype.setCallBackUrl = /**
* @param {?} callBackUrl
* @return {?}
*/
function (callBackUrl) {
this.callBackUrl = callBackUrl;
};
/**
* @param {?} scopes
* @return {?}
*/
SpotifyRgService.prototype.setScopes = /**
* @param {?} scopes
* @return {?}
*/
function (scopes) {
this.scopes = scopes;
};
/**
* @param {?} token
* @return {?}
*/
SpotifyRgService.prototype.setToken = /**
* @param {?} token
* @return {?}
*/
function (token) {
this.token = token;
};
/** @description Returns track
* @param artistId Tracks Spotify ID
*/
/**
* \@description Returns track
* @param {?} trackId
* @return {?}
*/
SpotifyRgService.prototype.getTrack = /**
* \@description Returns track
* @param {?} trackId
* @return {?}
*/
function (trackId) {
return this.apiGet("/tracks/" + trackId, null, this.getHeaders());
};
/** @description Returns tracks
* @param artistId Comma separated list of track ID's in a string.
*/
/**
* \@description Returns tracks
* @param {?} trackIds
* @return {?}
*/
SpotifyRgService.prototype.getTracks = /**
* \@description Returns tracks
* @param {?} trackIds
* @return {?}
*/
function (trackIds) {
/** @type {?} */
var t = encodeURIComponent(trackIds);
return this.apiGet("/tracks/?ids=" + t, null, this.getHeaders());
};
/** @description Returns album
* @param artistId Albums Spotify ID.
*/
/**
* \@description Returns album
* @param {?} albumId
* @return {?}
*/
SpotifyRgService.prototype.getAlbum = /**
* \@description Returns album
* @param {?} albumId
* @return {?}
*/
function (albumId) {
return this.apiGet("/albums/" + albumId, null, this.getHeaders());
};
/** @description Returns albums
* @param artistId Comma separated list of album ID's in a string..
*/
/**
* \@description Returns albums
* @param {?} albumIds
* @return {?}
*/
SpotifyRgService.prototype.getAlbums = /**
* \@description Returns albums
* @param {?} albumIds
* @return {?}
*/
function (albumIds) {
/** @type {?} */
var a = encodeURIComponent(albumIds);
return this.apiGet("/albums/?ids=" + a, null, this.getHeaders());
};
/** @description Returns albums tracks
* @param artistId Albums Spotify ID.
*/
/**
* \@description Returns albums tracks
* @param {?} albumId
* @return {?}
*/
SpotifyRgService.prototype.getAlbumsTracks = /**
* \@description Returns albums tracks
* @param {?} albumId
* @return {?}
*/
function (albumId) {
return this.apiGet("/albums/" + albumId + "/tracks", null, this.getHeaders());
};
/** @description Returns artist
* @param artistId Artists spotify ID.
*/
/**
* \@description Returns artist
* @param {?} artistId Artists spotify ID.
* @return {?}
*/
SpotifyRgService.prototype.getArtist = /**
* \@description Returns artist
* @param {?} artistId Artists spotify ID.
* @return {?}
*/
function (artistId) {
return this.apiGet("/artists/" + artistId, null, this.getHeaders());
};
/** @description Returns artists
* @param artistId Comma separated list of artist ID's in a string.
*/
/**
* \@description Returns artists
* @param {?} artistIds
* @return {?}
*/
SpotifyRgService.prototype.getArtists = /**
* \@description Returns artists
* @param {?} artistIds
* @return {?}
*/
function (artistIds) {
/** @type {?} */
var a = encodeURIComponent(artistIds);
return this.apiGet("/artists/?ids=" + a, null, this.getHeaders());
};
/** @description Returns artists albums
* @param artistId Artists Spotify ID.
*/
/**
* \@description Returns artists albums
* @param {?} artistId Artists Spotify ID.
* @return {?}
*/
SpotifyRgService.prototype.getArtistsAlbums = /**
* \@description Returns artists albums
* @param {?} artistId Artists Spotify ID.
* @return {?}
*/
function (artistId) {
return this.apiGet("/artists/" + artistId + "/albums", null, this.getHeaders());
};
/** @description Returns artists top tracks
* @param artistId Artists Spotify ID.
* @param countryCode Country code in 2 char Format. For example FI. Defaults to extracting country code from token.
*/
/**
* \@description Returns artists top tracks
* @param {?} artistId Artists Spotify ID.
* @param {?=} countryCode Country code in 2 char Format. For example FI. Defaults to extracting country code from token.
* @return {?}
*/
SpotifyRgService.prototype.getArtistsTopTracks = /**
* \@description Returns artists top tracks
* @param {?} artistId Artists Spotify ID.
* @param {?=} countryCode Country code in 2 char Format. For example FI. Defaults to extracting country code from token.
* @return {?}
*/
function (artistId, countryCode) {
/** @type {?} */
var countryCode = (countryCode != null && countryCode.length == 2) ? countryCode : "from_token";
return this.apiGet("/artists/" + artistId + "/top-tracks?country=" + countryCode, null, this.getHeaders());
};
/** @description Returns the user assosiated with auth token
* @param userId The Spotify User id
*/
/**
* \@description Returns the user assosiated with auth token
* @param {?} userId The Spotify User id
* @return {?}
*/
SpotifyRgService.prototype.getUserById = /**
* \@description Returns the user assosiated with auth token
* @param {?} userId The Spotify User id
* @return {?}
*/
function (userId) {
return this.apiGet('/users/' + userId, null, this.getHeaders());
};
/** @description Returns the user assosiated with auth token
*/
/**
* \@description Returns the user assosiated with auth token
* @return {?}
*/
SpotifyRgService.prototype.getUser = /**
* \@description Returns the user assosiated with auth token
* @return {?}
*/
function () {
return this.apiGet('/me', null, this.getHeaders());
};
/** @description Returns current users top tracks
* @param timeRange The range of time results are returned from. Use short_term, medium_term or long_term. Defaults to long_term.
* @param count Amount of tracks returned. Max value 50. Defaults to 50.
*/
/**
* \@description Returns current users top tracks
* @param {?} timeRange The range of time results are returned from. Use short_term, medium_term or long_term. Defaults to long_term.
* @param {?=} count Amount of tracks returned. Max value 50. Defaults to 50.
* @return {?}
*/
SpotifyRgService.prototype.getUsersTopTracks = /**
* \@description Returns current users top tracks
* @param {?} timeRange The range of time results are returned from. Use short_term, medium_term or long_term. Defaults to long_term.
* @param {?=} count Amount of tracks returned. Max value 50. Defaults to 50.
* @return {?}
*/
function (timeRange, count) {
/** @type {?} */
var params = {
//set backup default values
'time_range': timeRange == null ? 'long_term' : timeRange,
'limit': count == null ? '50' : count
};
return this.apiGet("/me/top/tracks", params, this.getHeaders());
};
/** @description Returns users playlists
* @param userId Users Spotify Id
*/
/**
* \@description Returns users playlists
* @param {?} userId Users Spotify Id
* @return {?}
*/
SpotifyRgService.prototype.getUsersPlaylists = /**
* \@description Returns users playlists
* @param {?} userId Users Spotify Id
* @return {?}
*/
function (userId) {
return this.apiGet('/users/' + userId + '/playlists', null, this.getHeaders());
};
/** @description Returns recommendations based on seeds. Max 5 seeds allowed (thats total so for example 3 artists and 2 tracks)
* @param seedArtists Comma separated list of artist seed Ids
* @param seedTracks Comma separated list of track seed Ids
*/
/**
* \@description Returns recommendations based on seeds. Max 5 seeds allowed (thats total so for example 3 artists and 2 tracks)
* @param {?} seedArtists Comma separated list of artist seed Ids
* @param {?} seedTracks Comma separated list of track seed Ids
* @return {?}
*/
SpotifyRgService.prototype.getRecommendedTracks = /**
* \@description Returns recommendations based on seeds. Max 5 seeds allowed (thats total so for example 3 artists and 2 tracks)
* @param {?} seedArtists Comma separated list of artist seed Ids
* @param {?} seedTracks Comma separated list of track seed Ids
* @return {?}
*/
function (seedArtists, seedTracks) {
/** @type {?} */
var sA = encodeURIComponent(seedArtists);
/** @type {?} */
var sT = encodeURIComponent(seedTracks);
return this.apiGet('/recommendations?limit=50&seed_artists=' + sA + '&seed_tracks=' + sT, null, this.getHeaders());
};
/** @description Get a list of new album releases featured in Spotify
* @param countryCode Country code in 2 char Format. If not set searches from all.
* @param count Amount of results returned. Defaults to 20.
*/
/**
* \@description Get a list of new album releases featured in Spotify
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
SpotifyRgService.prototype.getNewReleases = /**
* \@description Get a list of new album releases featured in Spotify
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
function (countryCode, count) {
/** @type {?} */
var query = "";
if (countryCode != null && countryCode != "") {
query += "?country=" + countryCode;
}
if (count != null && count != "") {
query += (query.length == 0) ? "?" : "&";
query += "limit=" + count;
}
return this.apiGet('/browse/new-releases' + query, null, this.getHeaders());
};
/** @description Get a list of Spotify featured playlists
* @param countryCode Country code in 2 char Format. If not set searches from all.
* @param count Amount of results returned. Defaults to 20.
*/
/**
* \@description Get a list of Spotify featured playlists
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
SpotifyRgService.prototype.getFeaturedPlaylists = /**
* \@description Get a list of Spotify featured playlists
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
function (countryCode, count) {
/** @type {?} */
var query = "";
if (countryCode != null && countryCode != "") {
query += "?country=" + countryCode;
}
if (count != null && count != "") {
query += (query.length == 0) ? "?" : "&";
query += "limit=" + count;
}
return this.apiGet('/browse/featured-playlists' + query, null, this.getHeaders());
};
/** @description Get a single category used to tag items in Spotify
* @param categoryId Spotifys Category Id
*/
/**
* \@description Get a single category used to tag items in Spotify
* @param {?} categoryId Spotifys Category Id
* @return {?}
*/
SpotifyRgService.prototype.getCategory = /**
* \@description Get a single category used to tag items in Spotify
* @param {?} categoryId Spotifys Category Id
* @return {?}
*/
function (categoryId) {
return this.apiGet('/browse/categories/' + categoryId, null, this.getHeaders());
};
/** @description Get a list of categories used to tag items in Spotify
* @param countryCode Country code in 2 char Format. If not set searches from all.
* @param count Amount of results returned. Defaults to 20.
*/
/**
* \@description Get a list of categories used to tag items in Spotify
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
SpotifyRgService.prototype.getCategories = /**
* \@description Get a list of categories used to tag items in Spotify
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
function (countryCode, count) {
/** @type {?} */
var query = "";
if (countryCode != null && countryCode != "") {
query += "?country=" + countryCode;
}
if (count != null && count != "") {
query += (query.length == 0) ? "?" : "&";
query += "limit=" + count;
}
return this.apiGet('/browse/categories' + query, null, this.getHeaders());
};
/** @description Get a list of Spotify playlists tagged with a particular category.
* @param categoryId The Spotify category ID for the category.
* @param countryCode Country code in 2 char Format. If not set searches from all.
* @param count Amount of results returned. Defaults to 20.
*/
/**
* \@description Get a list of Spotify playlists tagged with a particular category.
* @param {?} categoryId The Spotify category ID for the category.
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
SpotifyRgService.prototype.getCategorysPlaylists = /**
* \@description Get a list of Spotify playlists tagged with a particular category.
* @param {?} categoryId The Spotify category ID for the category.
* @param {?=} countryCode Country code in 2 char Format. If not set searches from all.
* @param {?=} count Amount of results returned. Defaults to 20.
* @return {?}
*/
function (categoryId, countryCode, count) {
/** @type {?} */
var query = "";
if (countryCode != null && countryCode != "") {
query += "?country=" + countryCode;
}
if (count != null && count != "") {
query += (query.length == 0) ? "?" : "&";
query += "limit=" + count;
}
return this.apiGet('/browse/categories/' + categoryId + '/playlists' + query, null, this.getHeaders());
};
/** @description Creates a playlist
* @param userId Users spotify ID.
* @param name A name for the playlist .
* @param description A description for the playlist.
* @param isPublic Defines if a playlist is public or private
*/
/**
* \@description Creates a playlist
* @param {?} userId Users spotify ID.
* @param {?} name A name for the playlist .
* @param {?} description A description for the playlist.
* @param {?} isPublic Defines if a playlist is public or private
* @return {?}
*/
SpotifyRgService.prototype.createPlayList = /**
* \@description Creates a playlist
* @param {?} userId Users spotify ID.
* @param {?} name A name for the playlist .
* @param {?} description A description for the playlist.
* @param {?} isPublic Defines if a playlist is public or private
* @return {?}
*/
function (userId, name, description, isPublic) {
/** @type {?} */
var body = {
"name": name,
"description": description,
"public": isPublic
};
return this.apiPost('/me/playlists?user_id=' + userId, body, this.getHeaders());
};
/** @description Adds tracks to playlist
* @param playlistId Playlist ID.
* @param trackUris Array of spotify track uris. Max 100 tracks can be added at a time.
*/
/**
* \@description Adds tracks to playlist
* @param {?} playListId
* @param {?} trackUris Array of spotify track uris. Max 100 tracks can be added at a time.
* @return {?}
*/
SpotifyRgService.prototype.addTracksToPlaylist = /**
* \@description Adds tracks to playlist
* @param {?} playListId
* @param {?} trackUris Array of spotify track uris. Max 100 tracks can be added at a time.
* @return {?}
*/
function (playListId, trackUris) {
/** @type {?} */
var body = {
"uris": trackUris,
};
return this.apiPost('/playlists/' + playListId + '/tracks', body, this.getHeaders());
};
/** @description Returns Spotify Catalog information about artists, albums, tracks or playlists that match a keyword string.
* @param query Search word.
* @param type A comma-separated list of item types to search across. Valid types are album, artist, playlist, and track.
* @param count The count is applied within each type, not on the total response. So count 3 with type album,track returns 3 albums and 3 tracks. Max 50.
*/
/**
* \@description Returns Spotify Catalog information about artists, albums, tracks or playlists that match a keyword string.
* @param {?} query Search word.
* @param {?} types
* @param {?} count The count is applied within each type, not on the total response. So count 3 with type album,track returns 3 albums and 3 tracks. Max 50.
* @return {?}
*/
SpotifyRgService.prototype.search = /**
* \@description Returns Spotify Catalog information about artists, albums, tracks or playlists that match a keyword string.
* @param {?} query Search word.
* @param {?} types
* @param {?} count The count is applied within each type, not on the total response. So count 3 with type album,track returns 3 albums and 3 tracks. Max 50.
* @return {?}
*/
function (query, types, count) {
/** @type {?} */
var q = encodeURIComponent(query);
/** @type {?} */
var t = encodeURIComponent(types);
return this.apiGet('/search?q=' + q + '&type=' + t + '&limit=' + count, null, this.getHeaders());
};
/**
* @private
* @param {?} endpoint
* @param {?} params
* @param {?} headers
* @return {?}
*/
SpotifyRgService.prototype.apiGet = /**
* @private
* @param {?} endpoint
* @param {?} params
* @param {?} headers
* @return {?}
*/
function (endpoint, params, headers) {
return this.http.get(this.baseUrl + endpoint, {
params: params,
headers: headers,
withCredentials: false
}).toPromise();
};
/**
* @private
* @param {?} endpoint
* @param {?} data
* @param {?} headers
* @return {?}
*/
SpotifyRgService.prototype.apiPost = /**
* @private
* @param {?} endpoint
* @param {?} data
* @param {?} headers
* @return {?}
*/
function (endpoint, data, headers) {
return this.http.post(this.baseUrl + endpoint, data, {
headers: headers,
})
.toPromise();
};
/**
* @private
* @return {?}
*/
SpotifyRgService.prototype.getHeaders = /**
* @private
* @return {?}
*/
function () {
return {
'Authorization': 'Bearer ' + this.token,
'Accept': 'application/json',
'Content-Type': 'application/json'
};
};
SpotifyRgService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
SpotifyRgService.ctorParameters = function () { return [
{ type: HttpClient }
]; };
/** @nocollapse */ SpotifyRgService.ngInjectableDef = i0.defineInjectable({ factory: function SpotifyRgService_Factory() { return new SpotifyRgService(i0.inject(i1.HttpClient)); }, token: SpotifyRgService, providedIn: "root" });
return SpotifyRgService;
}());
export { SpotifyRgService };
if (false) {
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.baseUrl;
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.token;
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.scopes;
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.callBackUrl;
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.clientId;
/**
* @type {?}
* @private
*/
SpotifyRgService.prototype.http;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BvdGlmeS1yZy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8vc3BvdGlmeS1zZXJ2aWNlLXJnLyIsInNvdXJjZXMiOlsibGliL3Nwb3RpZnktcmcuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7OztBQUlsRDtJQVdFLDBCQUE2QixJQUFnQjtRQUFoQixTQUFJLEdBQUosSUFBSSxDQUFZO1FBTnJDLFlBQU8sR0FBVyw0QkFBNEIsQ0FBQztJQU1OLENBQUM7Ozs7SUFFM0MsZ0NBQUs7OztJQUFaO1FBQ0UsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEdBQUcsd0NBQXdDO1lBQzdELHNCQUFzQjtZQUN0QixhQUFhLEdBQUcsSUFBSSxDQUFDLFFBQVE7WUFDN0IsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLEdBQUcsa0JBQWtCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDaEUsZ0JBQWdCLEdBQUcsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFRCwwQ0FBMEM7Ozs7OztJQUNuQyxzQ0FBVzs7Ozs7O0lBQWxCLFVBQW1CLFFBQWdCO1FBQ2pDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO0lBQzNCLENBQUM7Ozs7O0lBQ00seUNBQWM7Ozs7SUFBckIsVUFBc0IsV0FBbUI7UUFDdkMsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7SUFDakMsQ0FBQzs7Ozs7SUFDTSxvQ0FBUzs7OztJQUFoQixVQUFpQixNQUFjO1FBQzdCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7Ozs7O0lBQ00sbUNBQVE7Ozs7SUFBZixVQUFnQixL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LHVDQUE0Qjs7Ozs7SUFDNUIsb0NBQXlCOzs7OztJQUViLGdDQUFpQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEh0dHBDbGllbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyB0b1Byb21pc2UgfSBmcm9tICdyeGpzL29wZXJhdG9yL3RvUHJvbWlzZSc7XG5cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgU3BvdGlmeVJnU2VydmljZSB7XG5cbiAgcHJpdmF0ZSBiYXNlVXJsOiBzdHJpbmcgPSBcImh0dHBzOi8vYXBpLnNwb3RpZnkuY29tL3YxXCI7XG4gIHByaXZhdGUgdG9rZW46IHN0cmluZztcbiAgcHJpdmF0ZSBzY29wZXM6IHN0cmluZztcbiAgcHJpdmF0ZSBjYWxsQmFja1VybDogc3RyaW5nO1xuICBwcml2YXRlIGNsaWVudElkOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBodHRwOiBIdHRwQ2xpZW50KSB7IH1cblxuICBwdWJsaWMgbG9naW4oKSB7XG4gICAgd2luZG93LmxvY2F0aW9uLmhyZWYgPSAnaHR0cHM6Ly9hY2NvdW50cy5zcG90aWZ5LmNvbS9hdXRob3JpemUnICtcbiAgICAgICc/cmVzcG9uc2VfdHlwZT10b2tlbicgK1xuICAgICAgJyZjbGllbnRfaWQ9JyArIHRoaXMuY2xpZW50SWQgK1xuICAgICAgKHRoaXMuc2NvcGVzID8gJyZzY29wZT0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHRoaXMuc2NvcGVzKSA6ICcnKSArXG4gICAgICAnJnJlZGlyZWN0X3VyaT0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHRoaXMuY2FsbEJhY2tVcmwpO1xuICB9XG5cbiAgLy9TZXR0ZXJzIGZvciB0aGUgY29uZmlndXJhdGlvbiBwcm9wZXJ0aWVzXG4gIHB1YmxpYyBzZXRDbGllbnRJZChjbGllbnRJZDogc3RyaW5nKSB7XG4gICAgdGhpcy5jbGllbnRJZCA9IGNsaWVudElkO1xuICB9XG4gIHB1YmxpYyBzZXRDYWxsQmFja1VybChjYWxsQmFja1VybDogc3RyaW5nKSB7XG4gICAgdGhpcy5jYWxsQmFja1VybCA9IGNhbGxCYWNrVXJsO1xuICB9XG4gIHB1YmxpYyBzZXRTY29wZXMoc2NvcGVzOiBzdHJpbmcpIHtcbiAgICB0aGlzLnNjb3BlcyA9IHNjb3BlcztcbiAgfVxuICBwdWJsaWMgc2V0VG9rZW4odG9rZW46IHN0cmluZykge1xuICAgIHRoaXMudG9rZW4gPSB0b2tlbjtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gUmV0dXJucyB0cmFja1xuICogQHBhcmFtIGFydGlzdElkIFRyYWNrcyBTcG90aWZ5IElEXG4gKi9cbiAgcHVibGljIGdldFRyYWNrKHRyYWNrSWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLmFwaUdldChcIi90cmFja3MvXCIgKyB0cmFja0lkLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgdHJhY2tzXG4gICogQHBhcmFtIGFydGlzdElkIENvbW1hIHNlcGFyYXRlZCBsaXN0IG9mIHRyYWNrIElEJ3MgaW4gYSBzdHJpbmcuXG4gICovXG4gIHB1YmxpYyBnZXRUcmFja3ModHJhY2tJZHM6IHN0cmluZykge1xuICAgIGNvbnN0IHQgPSBlbmNvZGVVUklDb21wb25lbnQodHJhY2tJZHMpO1xuICAgIHJldHVybiB0aGlzLmFwaUdldChcIi90cmFja3MvP2lkcz1cIiArIHQsIG51bGwsIHRoaXMuZ2V0SGVhZGVycygpKTtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gUmV0dXJucyBhbGJ1bVxuICAqIEBwYXJhbSBhcnRpc3RJZCBBbGJ1bXMgU3BvdGlmeSBJRC5cbiAgKi9cbiAgcHVibGljIGdldEFsYnVtKGFsYnVtSWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLmFwaUdldChcIi9hbGJ1bXMvXCIgKyBhbGJ1bUlkLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgYWxidW1zXG4gICogQHBhcmFtIGFydGlzdElkIENvbW1hIHNlcGFyYXRlZCBsaXN0IG9mIGFsYnVtIElEJ3MgaW4gYSBzdHJpbmcuLlxuICAqL1xuICBwdWJsaWMgZ2V0QWxidW1zKGFsYnVtSWRzOiBzdHJpbmcpIHtcbiAgICBjb25zdCBhID0gZW5jb2RlVVJJQ29tcG9uZW50KGFsYnVtSWRzKTtcbiAgICByZXR1cm4gdGhpcy5hcGlHZXQoXCIvYWxidW1zLz9pZHM9XCIgKyBhLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgYWxidW1zIHRyYWNrc1xuICAqIEBwYXJhbSBhcnRpc3RJZCBBbGJ1bXMgU3BvdGlmeSBJRC5cbiAgKi9cbiAgcHVibGljIGdldEFsYnVtc1RyYWNrcyhhbGJ1bUlkOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5hcGlHZXQoXCIvYWxidW1zL1wiICsgYWxidW1JZCArIFwiL3RyYWNrc1wiLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgYXJ0aXN0XG4gICogQHBhcmFtIGFydGlzdElkIEFydGlzdHMgc3BvdGlmeSBJRC5cbiAgKi9cbiAgcHVibGljIGdldEFydGlzdChhcnRpc3RJZDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KFwiL2FydGlzdHMvXCIgKyBhcnRpc3RJZCwgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cbiAgLyoqIEBkZXNjcmlwdGlvbiBSZXR1cm5zIGFydGlzdHNcbiAgKiBAcGFyYW0gYXJ0aXN0SWQgQ29tbWEgc2VwYXJhdGVkIGxpc3Qgb2YgYXJ0aXN0IElEJ3MgaW4gYSBzdHJpbmcuXG4gICovXG4gIHB1YmxpYyBnZXRBcnRpc3RzKGFydGlzdElkczogc3RyaW5nKSB7XG4gICAgY29uc3QgYSA9IGVuY29kZVVSSUNvbXBvbmVudChhcnRpc3RJZHMpO1xuICAgIHJldHVybiB0aGlzLmFwaUdldChcIi9hcnRpc3RzLz9pZHM9XCIgKyBhLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgYXJ0aXN0cyBhbGJ1bXNcbiAgKiBAcGFyYW0gYXJ0aXN0SWQgQXJ0aXN0cyBTcG90aWZ5IElELlxuICAqL1xuICBwdWJsaWMgZ2V0QXJ0aXN0c0FsYnVtcyhhcnRpc3RJZDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KFwiL2FydGlzdHMvXCIgKyBhcnRpc3RJZCArIFwiL2FsYnVtc1wiLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgYXJ0aXN0cyB0b3AgdHJhY2tzXG4gICogQHBhcmFtIGFydGlzdElkIEFydGlzdHMgU3BvdGlmeSBJRC5cbiAgKiBAcGFyYW0gY291bnRyeUNvZGUgQ291bnRyeSBjb2RlIGluIDIgY2hhciBGb3JtYXQuIEZvciBleGFtcGxlIEZJLiBEZWZhdWx0cyB0byBleHRyYWN0aW5nIGNvdW50cnkgY29kZSBmcm9tIHRva2VuLlxuICAqL1xuICBwdWJsaWMgZ2V0QXJ0aXN0c1RvcFRyYWNrcyhhcnRpc3RJZDogc3RyaW5nLCBjb3VudHJ5Q29kZT86IHN0cmluZykge1xuICAgIHZhciBjb3VudHJ5Q29kZSA9IChjb3VudHJ5Q29kZSAhPSBudWxsICYmIGNvdW50cnlDb2RlLmxlbmd0aCA9PSAyKSA/IGNvdW50cnlDb2RlIDogXCJmcm9tX3Rva2VuXCI7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KFwiL2FydGlzdHMvXCIgKyBhcnRpc3RJZCArIFwiL3RvcC10cmFja3M/Y291bnRyeT1cIiArIGNvdW50cnlDb2RlLCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgdGhlIHVzZXIgYXNzb3NpYXRlZCB3aXRoIGF1dGggdG9rZW5cbiAgKiBAcGFyYW0gdXNlcklkIFRoZSBTcG90aWZ5IFVzZXIgaWRcbiAgKi9cbiAgcHVibGljIGdldFVzZXJCeUlkKHVzZXJJZDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvdXNlcnMvJyArIHVzZXJJZCwgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cbiAgLyoqIEBkZXNjcmlwdGlvbiBSZXR1cm5zIHRoZSB1c2VyIGFzc29zaWF0ZWQgd2l0aCBhdXRoIHRva2VuXG4gICovXG4gIHB1YmxpYyBnZXRVc2VyKCkge1xuICAgIHJldHVybiB0aGlzLmFwaUdldCgnL21lJywgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cbiAgLyoqIEBkZXNjcmlwdGlvbiBSZXR1cm5zIGN1cnJlbnQgdXNlcnMgdG9wIHRyYWNrc1xuICAqIEBwYXJhbSB0aW1lUmFuZ2UgVGhlIHJhbmdlIG9mIHRpbWUgcmVzdWx0cyBhcmUgcmV0dXJuZWQgZnJvbS4gVXNlIHNob3J0X3Rlcm0sIG1lZGl1bV90ZXJtIG9yIGxvbmdfdGVybS4gRGVmYXVsdHMgdG8gbG9uZ190ZXJtLlxuICAqIEBwYXJhbSBjb3VudCBBbW91bnQgb2YgdHJhY2tzIHJldHVybmVkLiBNYXggdmFsdWUgNTAuIERlZmF1bHRzIHRvIDUwLiBcbiAgKi9cbiAgcHVibGljIGdldFVzZXJzVG9wVHJhY2tzKHRpbWVSYW5nZSwgY291bnQ/OiBzdHJpbmcpOiBhbnkge1xuICAgIGNvbnN0IHBhcmFtcyA9IHtcbiAgICAgIC8vc2V0IGJhY2t1cCBkZWZhdWx0IHZhbHVlc1xuICAgICAgJ3RpbWVfcmFuZ2UnOiB0aW1lUmFuZ2UgPT0gbnVsbCA/ICdsb25nX3Rlcm0nIDogdGltZVJhbmdlLFxuICAgICAgJ2xpbWl0JzogY291bnQgPT0gbnVsbCA/ICc1MCcgOiBjb3VudFxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5hcGlHZXQoXCIvbWUvdG9wL3RyYWNrc1wiLCBwYXJhbXMsIHRoaXMuZ2V0SGVhZGVycygpKTtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gUmV0dXJucyB1c2VycyBwbGF5bGlzdHNcbiAgKiBAcGFyYW0gdXNlcklkIFVzZXJzIFNwb3RpZnkgSWRcbiAgKi9cbiAgcHVibGljIGdldFVzZXJzUGxheWxpc3RzKHVzZXJJZDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvdXNlcnMvJyArIHVzZXJJZCArICcvcGxheWxpc3RzJywgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cbiAgLyoqIEBkZXNjcmlwdGlvbiBSZXR1cm5zIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiBzZWVkcy4gTWF4IDUgc2VlZHMgYWxsb3dlZCAodGhhdHMgdG90YWwgc28gZm9yIGV4YW1wbGUgMyBhcnRpc3RzIGFuZCAyIHRyYWNrcylcbiAgKiBAcGFyYW0gc2VlZEFydGlzdHMgQ29tbWEgc2VwYXJhdGVkIGxpc3Qgb2YgYXJ0aXN0IHNlZWQgSWRzXG4gICogQHBhcmFtIHNlZWRUcmFja3MgQ29tbWEgc2VwYXJhdGVkIGxpc3Qgb2YgdHJhY2sgc2VlZCBJZHNcbiAgKi9cbiAgcHVibGljIGdldFJlY29tbWVuZGVkVHJhY2tzKHNlZWRBcnRpc3RzOiBzdHJpbmcsIHNlZWRUcmFja3M6IHN0cmluZyk6IGFueSB7XG4gICAgY29uc3Qgc0EgPSBlbmNvZGVVUklDb21wb25lbnQoc2VlZEFydGlzdHMpO1xuICAgIGNvbnN0IHNUID0gZW5jb2RlVVJJQ29tcG9uZW50KHNlZWRUcmFja3MpO1xuICAgIHJldHVybiB0aGlzLmFwaUdldCgnL3JlY29tbWVuZGF0aW9ucz9saW1pdD01MCZzZWVkX2FydGlzdHM9JyArIHNBICsgJyZzZWVkX3RyYWNrcz0nICsgc1QsIG51bGwsIHRoaXMuZ2V0SGVhZGVycygpKTtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gR2V0IGEgbGlzdCBvZiBuZXcgYWxidW0gcmVsZWFzZXMgZmVhdHVyZWQgaW4gU3BvdGlmeSBcbiAgKiBAcGFyYW0gY291bnRyeUNvZGUgQ291bnRyeSBjb2RlIGluIDIgY2hhciBGb3JtYXQuIElmIG5vdCBzZXQgc2VhcmNoZXMgZnJvbSBhbGwuXG4gICogQHBhcmFtIGNvdW50IEFtb3VudCBvZiByZXN1bHRzIHJldHVybmVkLiBEZWZhdWx0cyB0byAyMC5cbiAgKi9cbiAgcHVibGljIGdldE5ld1JlbGVhc2VzKGNvdW50cnlDb2RlPzogc3RyaW5nLCBjb3VudD86IHN0cmluZykge1xuICAgIHZhciBxdWVyeSA9IFwiXCI7XG4gICAgaWYgKGNvdW50cnlDb2RlICE9IG51bGwgJiYgY291bnRyeUNvZGUgIT0gXCJcIikge1xuICAgICAgcXVlcnkgKz0gXCI/Y291bnRyeT1cIiArIGNvdW50cnlDb2RlO1xuICAgIH1cbiAgICBpZiAoY291bnQgIT0gbnVsbCAmJiBjb3VudCAhPSBcIlwiKSB7XG4gICAgICBxdWVyeSArPSAocXVlcnkubGVuZ3RoID09IDApID8gXCI/XCIgOiBcIiZcIjtcbiAgICAgIHF1ZXJ5ICs9IFwibGltaXQ9XCIgKyBjb3VudDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvYnJvd3NlL25ldy1yZWxlYXNlcycgKyBxdWVyeSwgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cbiAgLyoqIEBkZXNjcmlwdGlvbiBHZXQgYSBsaXN0IG9mIFNwb3RpZnkgZmVhdHVyZWQgcGxheWxpc3RzXG4gICogQHBhcmFtIGNvdW50cnlDb2RlIENvdW50cnkgY29kZSBpbiAyIGNoYXIgRm9ybWF0LiBJZiBub3Qgc2V0IHNlYXJjaGVzIGZyb20gYWxsLlxuICAqIEBwYXJhbSBjb3VudCBBbW91bnQgb2YgcmVzdWx0cyByZXR1cm5lZC4gRGVmYXVsdHMgdG8gMjAuXG4gICovXG4gIHB1YmxpYyBnZXRGZWF0dXJlZFBsYXlsaXN0cyhjb3VudHJ5Q29kZT86IHN0cmluZywgY291bnQ/OiBzdHJpbmcpIHtcbiAgICB2YXIgcXVlcnkgPSBcIlwiO1xuICAgIGlmIChjb3VudHJ5Q29kZSAhPSBudWxsICYmIGNvdW50cnlDb2RlICE9IFwiXCIpIHtcbiAgICAgIHF1ZXJ5ICs9IFwiP2NvdW50cnk9XCIgKyBjb3VudHJ5Q29kZTtcbiAgICB9XG4gICAgaWYgKGNvdW50ICE9IG51bGwgJiYgY291bnQgIT0gXCJcIikge1xuICAgICAgcXVlcnkgKz0gKHF1ZXJ5Lmxlbmd0aCA9PSAwKSA/IFwiP1wiIDogXCImXCI7XG4gICAgICBxdWVyeSArPSBcImxpbWl0PVwiICsgY291bnQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFwaUdldCgnL2Jyb3dzZS9mZWF0dXJlZC1wbGF5bGlzdHMnICsgcXVlcnksIG51bGwsIHRoaXMuZ2V0SGVhZGVycygpKTtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gR2V0IGEgc2luZ2xlIGNhdGVnb3J5IHVzZWQgdG8gdGFnIGl0ZW1zIGluIFNwb3RpZnkgXG4gICogQHBhcmFtIGNhdGVnb3J5SWQgU3BvdGlmeXMgQ2F0ZWdvcnkgSWRcbiAgKi9cbiAgcHVibGljIGdldENhdGVnb3J5KGNhdGVnb3J5SWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLmFwaUdldCgnL2Jyb3dzZS9jYXRlZ29yaWVzLycrY2F0ZWdvcnlJZCwgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cblxuICAvKiogQGRlc2NyaXB0aW9uIEdldCBhIGxpc3Qgb2YgY2F0ZWdvcmllcyB1c2VkIHRvIHRhZyBpdGVtcyBpbiBTcG90aWZ5XG4gICogQHBhcmFtIGNvdW50cnlDb2RlIENvdW50cnkgY29kZSBpbiAyIGNoYXIgRm9ybWF0LiBJZiBub3Qgc2V0IHNlYXJjaGVzIGZyb20gYWxsLlxuICAqIEBwYXJhbSBjb3VudCBBbW91bnQgb2YgcmVzdWx0cyByZXR1cm5lZC4gRGVmYXVsdHMgdG8gMjAuXG4gICovXG4gIHB1YmxpYyBnZXRDYXRlZ29yaWVzKGNvdW50cnlDb2RlPzogc3RyaW5nLCBjb3VudD86IHN0cmluZykge1xuICAgIHZhciBxdWVyeSA9IFwiXCI7XG4gICAgaWYgKGNvdW50cnlDb2RlICE9IG51bGwgJiYgY291bnRyeUNvZGUgIT0gXCJcIikge1xuICAgICAgcXVlcnkgKz0gXCI/Y291bnRyeT1cIiArIGNvdW50cnlDb2RlO1xuICAgIH1cbiAgICBpZiAoY291bnQgIT0gbnVsbCAmJiBjb3VudCAhPSBcIlwiKSB7XG4gICAgICBxdWVyeSArPSAocXVlcnkubGVuZ3RoID09IDApID8gXCI/XCIgOiBcIiZcIjtcbiAgICAgIHF1ZXJ5ICs9IFwibGltaXQ9XCIgKyBjb3VudDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvYnJvd3NlL2NhdGVnb3JpZXMnICsgcXVlcnksIG51bGwsIHRoaXMuZ2V0SGVhZGVycygpKTtcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gR2V0IGEgbGlzdCBvZiBTcG90aWZ5IHBsYXlsaXN0cyB0YWdnZWQgd2l0aCBhIHBhcnRpY3VsYXIgY2F0ZWdvcnkuXG4gICogQHBhcmFtIGNhdGVnb3J5SWQgVGhlIFNwb3RpZnkgY2F0ZWdvcnkgSUQgZm9yIHRoZSBjYXRlZ29yeS5cbiAgKiBAcGFyYW0gY291bnRyeUNvZGUgQ291bnRyeSBjb2RlIGluIDIgY2hhciBGb3JtYXQuIElmIG5vdCBzZXQgc2VhcmNoZXMgZnJvbSBhbGwuXG4gICogQHBhcmFtIGNvdW50IEFtb3VudCBvZiByZXN1bHRzIHJldHVybmVkLiBEZWZhdWx0cyB0byAyMC5cbiAgKi9cbiAgcHVibGljIGdldENhdGVnb3J5c1BsYXlsaXN0cyhjYXRlZ29yeUlkOiBzdHJpbmcsIGNvdW50cnlDb2RlPzogc3RyaW5nLCBjb3VudD86IHN0cmluZykge1xuICAgIHZhciBxdWVyeSA9IFwiXCI7XG4gICAgaWYgKGNvdW50cnlDb2RlICE9IG51bGwgJiYgY291bnRyeUNvZGUgIT0gXCJcIikge1xuICAgICAgcXVlcnkgKz0gXCI/Y291bnRyeT1cIiArIGNvdW50cnlDb2RlO1xuICAgIH1cbiAgICBpZiAoY291bnQgIT0gbnVsbCAmJiBjb3VudCAhPSBcIlwiKSB7XG4gICAgICBxdWVyeSArPSAocXVlcnkubGVuZ3RoID09IDApID8gXCI/XCIgOiBcIiZcIjtcbiAgICAgIHF1ZXJ5ICs9IFwibGltaXQ9XCIgKyBjb3VudDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvYnJvd3NlL2NhdGVnb3JpZXMvJytjYXRlZ29yeUlkKycvcGxheWxpc3RzJyArIHF1ZXJ5LCBudWxsLCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIENyZWF0ZXMgYSBwbGF5bGlzdCBcbiAgKiBAcGFyYW0gdXNlcklkIFVzZXJzIHNwb3RpZnkgSUQuXG4gICogQHBhcmFtIG5hbWUgQSBuYW1lIGZvciB0aGUgcGxheWxpc3QgLlxuICAqIEBwYXJhbSBkZXNjcmlwdGlvbiBBIGRlc2NyaXB0aW9uIGZvciB0aGUgcGxheWxpc3QuXG4gICogQHBhcmFtIGlzUHVibGljIERlZmluZXMgaWYgYSBwbGF5bGlzdCBpcyBwdWJsaWMgb3IgcHJpdmF0ZVxuICAqL1xuICBwdWJsaWMgY3JlYXRlUGxheUxpc3QodXNlcklkOiBudW1iZXIsIG5hbWU6IHN0cmluZywgZGVzY3JpcHRpb246IHN0cmluZywgaXNQdWJsaWM6IGJvb2xlYW4pIHtcbiAgICB2YXIgYm9keSA9IHtcbiAgICAgIFwibmFtZVwiOiBuYW1lLFxuICAgICAgXCJkZXNjcmlwdGlvblwiOiBkZXNjcmlwdGlvbixcbiAgICAgIFwicHVibGljXCI6IGlzUHVibGljXG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFwaVBvc3QoJy9tZS9wbGF5bGlzdHM/dXNlcl9pZD0nICsgdXNlcklkLCBib2R5LCB0aGlzLmdldEhlYWRlcnMoKSlcbiAgfVxuXG4gIC8qKiBAZGVzY3JpcHRpb24gQWRkcyB0cmFja3MgdG8gcGxheWxpc3RcbiAgKiBAcGFyYW0gcGxheWxpc3RJZCBQbGF5bGlzdCBJRC5cbiAgKiBAcGFyYW0gdHJhY2tVcmlzIEFycmF5IG9mIHNwb3RpZnkgdHJhY2sgdXJpcy4gTWF4IDEwMCB0cmFja3MgY2FuIGJlIGFkZGVkIGF0IGEgdGltZS5cbiAgKi9cbiAgcHVibGljIGFkZFRyYWNrc1RvUGxheWxpc3QocGxheUxpc3RJZDogc3RyaW5nLCB0cmFja1VyaXM6IHN0cmluZ1tdKSB7XG4gICAgdmFyIGJvZHkgPSB7XG4gICAgICBcInVyaXNcIjogdHJhY2tVcmlzLFxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5hcGlQb3N0KCcvcGxheWxpc3RzLycgKyBwbGF5TGlzdElkICsgJy90cmFja3MnLCBib2R5LCB0aGlzLmdldEhlYWRlcnMoKSk7XG4gIH1cblxuICAvKiogQGRlc2NyaXB0aW9uIFJldHVybnMgU3BvdGlmeSBDYXRhbG9nIGluZm9ybWF0aW9uIGFib3V0IGFydGlzdHMsIGFsYnVtcywgdHJhY2tzIG9yIHBsYXlsaXN0cyB0aGF0IG1hdGNoIGEga2V5d29yZCBzdHJpbmcuXG4gICogQHBhcmFtIHF1ZXJ5IFNlYXJjaCB3b3JkLlxuICAqIEBwYXJhbSB0eXBlIEEgY29tbWEtc2VwYXJhdGVkIGxpc3Qgb2YgaXRlbSB0eXBlcyB0byBzZWFyY2ggYWNyb3NzLiBWYWxpZCB0eXBlcyBhcmUgYWxidW0sIGFydGlzdCwgcGxheWxpc3QsIGFuZCB0cmFjay5cbiAgKiBAcGFyYW0gY291bnQgVGhlIGNvdW50IGlzIGFwcGxpZWQgd2l0aGluIGVhY2ggdHlwZSwgbm90IG9uIHRoZSB0b3RhbCByZXNwb25zZS4gU28gY291bnQgMyB3aXRoIHR5cGUgYWxidW0sdHJhY2sgcmV0dXJucyAzIGFsYnVtcyBhbmQgMyB0cmFja3MuIE1heCA1MC5cbiAgKi9cbiAgcHVibGljIHNlYXJjaChxdWVyeTogc3RyaW5nLCB0eXBlczogc3RyaW5nLCBjb3VudDogc3RyaW5nKSB7XG4gICAgY29uc3QgcSA9IGVuY29kZVVSSUNvbXBvbmVudChxdWVyeSk7XG4gICAgY29uc3QgdCA9IGVuY29kZVVSSUNvbXBvbmVudCh0eXBlcyk7XG4gICAgcmV0dXJuIHRoaXMuYXBpR2V0KCcvc2VhcmNoP3E9JyArIHEgKyAnJnR5cGU9JyArIHQgKyAnJmxpbWl0PScgKyBjb3VudCwgbnVsbCwgdGhpcy5nZXRIZWFkZXJzKCkpO1xuICB9XG5cblxuICBwcml2YXRlIGFwaUdldChlbmRwb2ludDogc3RyaW5nLCBwYXJhbXMsIGhlYWRlcnMpIHtcbiAgICByZXR1cm4gdGhpcy5odHRwLmdldCh0aGlzLmJhc2VVcmwgKyBlbmRwb2ludCwge1xuICAgICAgcGFyYW1zOiBwYXJhbXMsXG4gICAgICBoZWFkZXJzOiBoZWFkZXJzLFxuICAgICAgd2l0aENyZWRlbnRpYWxzOiBmYWxzZVxuICAgIH0pLnRvUHJvbWlzZSgpXG4gIH1cblxuICBwcml2YXRlIGFwaVBvc3QoZW5kcG9pbnQ6IHN0cmluZywgZGF0YSwgaGVhZGVycykge1xuICAgIHJldHVybiB0aGlzLmh0dHAucG9zdCh0aGlzLmJhc2VVcmwgKyBlbmRwb2ludCwgZGF0YSwge1xuICAgICAgaGVhZGVyczogaGVhZGVycyxcbiAgICB9KVxuICAgICAgLnRvUHJvbWlzZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRIZWFkZXJzKCkge1xuICAgIHJldHVybiB7XG4gICAgICAnQXV0aG9yaXphdGlvbic6ICdCZWFyZXIgJyArIHRoaXMudG9rZW4sXG4gICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgIH1cbiAgfVxufVxuIl19