UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

205 lines (177 loc) • 7.28 kB
import { Component } from '../Base/Component'; import { IComponentBindings } from '../Base/ComponentBindings'; import { ComponentOptions } from '../Base/ComponentOptions'; import { Assert } from '../../misc/Assert'; import { QueryEvents, IQuerySuccessEventArgs } from '../../events/QueryEvents'; import { ITriggerNotify, ITriggerExecute, ITriggerRedirect, ITriggerQuery, ITrigger } from '../../rest/Trigger'; import { $$ } from '../../utils/Dom'; import { IAnalyticsTriggerNotify, analyticsActionCauseList, IAnalyticsTriggerRedirect, IAnalyticsTriggerQuery, IAnalyticsTriggerExecute } from '../Analytics/AnalyticsActionListMeta'; import { QueryStateModel } from '../../models/QueryStateModel'; import { Initialization } from '../Base/Initialization'; import { object, map } from 'underscore'; import { exportGlobally } from '../../GlobalExports'; import 'styling/_Triggers'; import { IQuery } from '../../rest/Query'; import { Utils } from '../../UtilsModules'; export interface ITriggersOptions {} function isTriggerNotify(trigger: ITrigger<any>): trigger is ITriggerNotify { return trigger.type === 'notify'; } function isTriggerRedirect(trigger: ITrigger<any>): trigger is ITriggerRedirect { return trigger.type === 'redirect'; } function isTriggerQuery(trigger: ITrigger<any>): trigger is ITriggerQuery { return trigger.type === 'query'; } function isTriggerExecute(trigger: ITrigger<any>): trigger is ITriggerExecute { return trigger.type === 'execute'; } /** * The Triggers component enables the use of triggers (`notify`, `execute`, `query`, `redirect`) generated by the Coveo * Search API (see [Trigger](https://docs.coveo.com/en/1458/)) in the query pipeline (see * [Managing the Query Pipeline](https://docs.coveo.com/en/1450/)). * * To enable the triggers functionality in your JavaScript Search Framework interface, incorporate a div with the `coveoTriggers` class. For example: `<div class="coveoTriggers"></div>`. * * Note: adding the Triggers component gives query pipeline administrators the power to influence users' search experience. * Bad actors will be able to perform XSS attacks, or redirect users to dangerous sites. Make sure only individuals you trust * have query pipeline edit privileges. */ export class Triggers extends Component { static ID = 'Triggers'; static options: ITriggersOptions = {}; static doExport = () => { exportGlobally({ Triggers: Triggers }); }; /** * The list of notifications returned by the Search API for the current query (via `notify` triggers). * * The Triggers component automatically renders this list visually. */ public notifications: string[]; /** * Creates a new Triggers component. * @param element The HTMLElement on which to instantiate the component. * @param options The options for the Triggers component. * @param bindings The bindings that the component requires to function normally. If not set, these will be * automatically resolved (with a slower execution time). * @param _window The window on which to execute the triggers. */ constructor( public element: HTMLElement, public options?: ITriggersOptions, public bindings?: IComponentBindings, public _window?: Window ) { super(element, Triggers.ID, bindings); this._window = this._window || window; this.options = ComponentOptions.initComponentOptions(element, Triggers, options); Assert.exists(element); Assert.exists(this.options); this.notifications = []; this.bind.onRootElement(QueryEvents.querySuccess, this.handleProcessNewQueryResults); } private handleProcessNewQueryResults(data: IQuerySuccessEventArgs) { Assert.exists(data); Assert.exists(data.results); $$(this.element).empty(); this.notifications = []; if (Utils.isNullOrUndefined(data.results.triggers)) { $$(this.element).removeClass('coveo-visible'); return; } const notifyTriggers: ITriggerNotify[] = []; let redirectTrigger: ITriggerRedirect | null = null; let queryTrigger: ITriggerQuery | null = null; let executeTrigger: ITriggerExecute | null = null; data.results.triggers.forEach(trigger => { if (isTriggerNotify(trigger)) { notifyTriggers.push(trigger); } else if (isTriggerRedirect(trigger)) { redirectTrigger = redirectTrigger || trigger; } else if (isTriggerQuery(trigger)) { queryTrigger = queryTrigger || trigger; } else if (isTriggerExecute(trigger)) { executeTrigger = executeTrigger || trigger; } }); if (notifyTriggers.length) { this.processNotifyTriggers(notifyTriggers); $$(this.element).addClass('coveo-visible'); } redirectTrigger && this.processRedirectTrigger(redirectTrigger); queryTrigger && this.processQueryTrigger(queryTrigger); executeTrigger && this.processExecuteTrigger(executeTrigger, data.query); } private processNotifyTriggers(triggers: ITriggerNotify[]) { this.usageAnalytics.logCustomEvent<IAnalyticsTriggerNotify>( analyticsActionCauseList.triggerNotify, { notifications: triggers.map(trigger => trigger.content) }, this.element ); triggers.forEach(trigger => { this.notifications.push(trigger.content); this.element.appendChild($$('div', { className: 'coveo-trigger-notify' }, trigger.content).el); }); } private processRedirectTrigger(trigger: ITriggerRedirect) { this.usageAnalytics.logCustomEvent<IAnalyticsTriggerRedirect>( analyticsActionCauseList.triggerRedirect, { redirectedTo: trigger.content }, this.element ); this._window.location.replace(trigger.content); } private processQueryTrigger(trigger: ITriggerQuery) { this.queryStateModel.set(QueryStateModel.attributesEnum.q, trigger.content); this.queryController.executeQuery({ beforeExecuteQuery: () => { this.usageAnalytics.logCustomEvent<IAnalyticsTriggerQuery>( analyticsActionCauseList.triggerQuery, { query: trigger.content }, this.element ); } }); } private processExecuteTrigger(trigger: ITriggerExecute, query: IQuery) { try { let func: Function = this._window['' + trigger.content.name]; if (typeof func === 'function') { let params = object( map(trigger.content.params, (value, index) => { return ['param' + (index + 1), value]; }) ); params['element'] = this.element; this.usageAnalytics.logCustomEvent<IAnalyticsTriggerExecute>( analyticsActionCauseList.triggerExecute, { executions: [{ functionName: trigger.content.name, params: trigger.content.params }] }, this.element ); func.apply(this._window, [params]); } else { this.logger.error(`A trigger tried to call the function '${trigger.content.name}', which doesn't exist.`, this, query, trigger); } } catch (error) { this.logger.error(`A trigger called the function '${trigger.content.name}', which threw an error.`, this, query, trigger); } } } Initialization.registerAutoCreateComponent(Triggers);