@playkit-js/kaltura-player-js
Version:
[](https://github.com/kaltura/kaltura-player-js/actions/workflows/run_canary.yaml) [ • 7.45 kB
text/typescript
import { PluginConfigStore, templateRegex } from 'plugins-config-store';
import evaluate from '../utils/evaluate';
import { getReferrer } from '../utils/kaltura-params';
import { Utils, getLogger } from '@playkit-js/playkit-js';
import { getServerUIConf } from '../utils/setup-helpers';
import { KalturaPlayerConfig, PluginsConfig } from '../../types';
const logger = getLogger('PluginsConfig');
/**
* returns whether value is evaluated
* @private
* @param {*} value - the value to be checked
* @returns {boolean} - value is evaluated
*/
const isValueEvaluated = (value: any): boolean =>
(typeof value === 'number' ||
typeof value === 'function' ||
typeof value === 'string' ||
typeof value === 'boolean' ||
Utils.Object.isClassInstance(value)) &&
!templateRegex.test(value.toString());
/**
* returns whether the value is a simple object (not a function or class instance)
* @private
* @param {*} value - the value to be checked
* @returns {boolean} - whether the value is a simple object or not
*/
const isSimpleObject = (value: any): boolean => Utils.Object.isObject(value) && typeof value !== 'function' && !Utils.Object.isClassInstance(value);
/**
* filters out unevaluated expressions in an array
* @private
* @param {Array} value - the array to be checked
* @returns {Array} - the array with unevaluated expressions filtered out
*/
const filterUnevaluatedExpressions = (value: ReadonlyArray<any>): ReadonlyArray<any> => {
return value
.map((item) => {
if (isSimpleObject(item)) {
const updatedItem = removeUnevaluatedExpression(item);
return Utils.Object.isEmptyObject(updatedItem) ? null : updatedItem;
} else if (isValueEvaluated(item)) {
return item;
} else {
return null;
}
})
.filter((item) => item !== null);
};
/**
* remove unevaluated expressions form object
* @private
* @param {Object} obj - the object examine
* @returns {Object} - the object without unevaluated strings
*/
const removeUnevaluatedExpression = (obj: any = {}): any =>
Object.entries(obj).reduce((product, [key, value]): any => {
if (isSimpleObject(value)) {
product[key] = removeUnevaluatedExpression(value);
} else if (Array.isArray(value)) {
product[key] = filterUnevaluatedExpressions(value);
} else if (isValueEvaluated(value)) {
product[key] = value;
}
return product;
}, {});
/**
* returns the data model for evaluating evaluation tokens
* @private
* @param {KPOptionsObject} options - the kaltura player options object
* @returns {Object} - data model
*/
const getModel = (options: Partial<KalturaPlayerConfig>): any => {
const dataModel: any = {
pVersion: getServerUIConf()?.productVersion || __VERSION__,
pName: __NAME__
};
if (options.targetId) {
dataModel.domRootElementId = options.targetId;
}
if (options.provider && options.provider.env) {
dataModel['serviceUrl'] = options.provider.env.serviceUrl;
const analyticsServiceUrl = Utils.Object.getPropertyPath(options, 'provider.env.analyticsServiceUrl');
if (analyticsServiceUrl) {
dataModel['analyticsServiceUrl'] = `${analyticsServiceUrl}/api_v3/index.php`;
}
if (dataModel['serviceUrl']) {
dataModel['embedBaseUrl'] = dataModel['serviceUrl'].replace('api_v3', '');
}
}
const entryDataModel = {
referrer: getReferrer(),
encodedReferrer: getEncodedReferrer()
};
if (options.provider) {
Utils.Object.mergeDeep(entryDataModel, {
ks: options.provider.ks,
uiConfId: options.provider.uiConfId,
partnerId: options.provider.partnerId
});
}
if (options.session) {
Utils.Object.mergeDeep(entryDataModel, {
sessionId: options.session.id,
ks: options.session.ks,
isAnonymous: options.session.isAnonymous,
uiConfId: options.session.uiConfId,
partnerId: options.session.partnerId
});
}
if (options.sources) {
Utils.Object.mergeDeep(entryDataModel, {
entryId: options.sources.id,
entryName: options.sources.metadata && options.sources.metadata.name,
entryType: options.sources.type
});
}
if (options.playlist) {
Utils.Object.mergeDeep(entryDataModel, {
playlistId: options.playlist.id
});
}
Object.keys(entryDataModel).forEach((key) => {
if (entryDataModel[key] === undefined) {
delete entryDataModel[key];
}
});
Utils.Object.mergeDeep(dataModel, entryDataModel);
const { uiConfData } = options;
if (uiConfData) {
dataModel['embedWidth'] = uiConfData.width;
dataModel['embedHeight'] = uiConfData.height;
}
return dataModel;
};
/**
* @return {string} - The referrer after URIComponent encoded
* @private
*/
function getEncodedReferrer(): string {
const referrer = getReferrer();
return encodeURIComponent(referrer);
}
/**
* @private
* @param {string} text - the string to sanitize
* @returns {string} - the sanitized string
* @private
*/
function _sanitize(text: string): string {
if (!text) return '';
return (
text
.replace(/\\n/g, '\\n')
.replace(/\\'/g, "\\'")
.replace(/\\"/g, '\\"')
.replace(/\\&/g, '\\&')
.replace(/\\r/g, '\\r')
.replace(/\\t/g, '\\t')
.replace(/\\b/g, '\\b')
.replace(/\\f/g, '\\f')
// remove non-printable and other non-valid JSON chars
// eslint-disable-next-line no-control-regex
.replace(/[\u0000-\u0019]+/g, '')
);
}
/**
*
* @param {string} config - the config string
* @returns {Object} - the config object
* @private
*/
function _formatConfigString(config: string): any {
let configObj;
config = _sanitize(config);
try {
configObj = JSON.parse(config, function (key) {
try {
return JSON.parse(this[key]);
} catch (e) {
return this[key];
}
});
} catch (e) {
logger.error('An error occurred while formatting config string.', e);
configObj = {};
}
return configObj;
}
/**
* @param {Object} data - target config object
* @param {Object} evaluatedConfig - the evaluated object
* @private
* @returns {void}
*/
function _mergeConfig(data: any, evaluatedConfig: any): void {
const evaluatedCleanConfig = removeUnevaluatedExpression(evaluatedConfig);
const cleanData = removeUnevaluatedExpression(data);
if (cleanData && evaluatedCleanConfig) {
Object.keys(data).forEach((pluginName) => {
if (data && data[pluginName]) {
data[pluginName] = Utils.Object.mergeDeep({}, evaluatedCleanConfig[pluginName], cleanData[pluginName]);
}
});
}
}
class ConfigEvaluator {
private _pluginConfigStore: PluginConfigStore;
/**
* constructor
* @constructor
*/
constructor() {
this._pluginConfigStore = new PluginConfigStore();
}
/**
* @param {KPPluginsConfigObject} options - plugins options
* @param {KPOptionsObject} config - player config
* @return {void}
*/
public evaluatePluginsConfig(options: PluginsConfig | undefined, config: Partial<KalturaPlayerConfig>): void {
if (options) {
this._pluginConfigStore.set(options);
const dataModel = getModel(config);
const mergedConfig = Utils.Object.mergeDeep({}, this._pluginConfigStore.get(), options);
const evaluatedConfig = _formatConfigString(evaluate(JSON.stringify(mergedConfig), dataModel));
_mergeConfig(options, evaluatedConfig);
}
}
}
export { ConfigEvaluator, getEncodedReferrer };