UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

173 lines (149 loc) 6.23 kB
import * as _ from 'underscore'; import { exportGlobally } from '../../GlobalExports'; import { IQueryResult } from '../../rest/QueryResult'; import { ISearchEndpoint } from '../../rest/SearchEndpointInterface'; import { $$ } from '../../utils/Dom'; import { QueryUtils } from '../../utils/QueryUtils'; import { Component } from '../Base/Component'; import { ComponentOptions } from '../Base/ComponentOptions'; import { Initialization } from '../Base/Initialization'; import { get } from '../Base/RegisteredNamedMethods'; import { IResultsComponentBindings } from '../Base/ResultsComponentBindings'; import { Icon } from '../Icon/Icon'; import { ResultLink } from '../ResultLink/ResultLink'; import { IResultLinkOptions } from '../ResultLink/ResultLinkOptions'; import { FieldTable } from '../FieldTable/FieldTable'; import { l } from '../../strings/Strings'; export interface IThumbnailOptions extends IResultLinkOptions { noThumbnailClass?: string; clickable?: boolean; } /** * The Thumbnail component automatically fetches the thumbnail of the result object and outputs an HTML `img` tag with * it. * @notSupportedIn salesforcefree */ export class Thumbnail extends Component { static ID = 'Thumbnail'; static doExport = () => { exportGlobally({ Thumbnail: Thumbnail }); }; /** * Options for the Thumbnail * @componentOptions */ static options: IThumbnailOptions = { /** * Specifies the CSS class to use on the `img` tag that the Thumbnail component outputs when a result has no * thumbnail in the index. * * Default value is `coveo-no-thumbnail`. */ noThumbnailClass: ComponentOptions.buildStringOption({ defaultValue: 'coveo-no-thumbnail' }), /** * Specifies whether to create a clickable {@link ResultLink} around the Thumbnail. * * Default value is `false`. * * If set to true, you can use the options specified on {@link ResultLink.options} */ clickable: ComponentOptions.buildBooleanOption({ defaultValue: false }) }; static parent = ResultLink; public img: HTMLImageElement; /** * Creates a new Thumbnail component. * @param element The HTMLElement on which to instantiate the component. * @param options The options for the Thumbnail 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 result The result to associate the component with. */ constructor( public element: HTMLElement, public options?: IThumbnailOptions, public bindings?: IResultsComponentBindings, public result?: IQueryResult ) { super(element, Thumbnail.ID, bindings); this.options = ComponentOptions.initOptions(element, <any>Thumbnail.options, options, Thumbnail.ID); if (this.element.tagName.toLowerCase() != 'img') { this.img = <HTMLImageElement>$$('img').el; this.element.appendChild(this.img); } else { this.img = <HTMLImageElement>this.element; } if (this.options.clickable) { if (this.element.tagName.toLowerCase() != 'img') { new ResultLink(this.element, this.options, this.bindings, this.result); } else { let href = $$('a'); $$(this.element).replaceWith(href.el); $$(href).append(this.element); new ResultLink(href.el, this.options, this.bindings, this.result); } } // We need to set src to a blank image right away to avoid the image from // changing size once it's loaded. Also, doing this prevents a border from // appearing on some browsers when there is no thumbnail. I've found no other // way to get rid of it... this.img.setAttribute('src', 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='); if (QueryUtils.hasThumbnail(result)) { this.buildThumbnailImage(); } else { this.logger.info('Result has no thumbnail. Cannot build thumbnail image, instanciating an Icon component instead.'); const icn = new Icon($$('div').el, { small: true }, bindings, result); $$(this.element).replaceWith(icn.element); } } private buildThumbnailImage() { let endpoint = this.bindings.queryController.getEndpoint(); if (endpoint.isJsonp()) { // For jsonp we can't GET/POST for binary data. We are limited // to only setting the src attribute directly on the img. this.buildImageWithDirectSrcAttribute(endpoint); } else { // Base 64 img allows us to GET/POST the image as raw binary, so that we can also // pass the credential of the user. Useful for phonegap among others. this.buildImageWithBase64SrcAttribute(endpoint); } this.makeAccessible(); } private buildImageWithDirectSrcAttribute(endpoint: ISearchEndpoint) { let dataStreamUri = endpoint.getViewAsDatastreamUri(this.result.uniqueId, '$Thumbnail$', { contentType: 'image/png' }); this.img.setAttribute('src', dataStreamUri); this.resizeContainingFieldTable(); } private buildImageWithBase64SrcAttribute(endpoint: ISearchEndpoint) { endpoint .getRawDataStream(this.result.uniqueId, '$Thumbnail$') .then(response => { let rawBinary = String.fromCharCode.apply(null, new Uint8Array(response)); this.img.setAttribute('src', 'data:image/png;base64, ' + btoa(rawBinary)); this.resizeContainingFieldTable(); }) .catch(() => { this.setEmptyThumbnailClass(); }); } private makeAccessible() { this.img.setAttribute('alt', l('ThumbnailOf', this.result.title)); } private resizeContainingFieldTable() { const className = Component.computeCssClassNameForType('FieldTable'); let closestFieldTableElement = $$(this.element).closest(className); if (closestFieldTableElement != null) { let fieldTable = <FieldTable>get(closestFieldTableElement); fieldTable.updateToggleHeight(); } } private setEmptyThumbnailClass() { $$(this.img).addClass(this.options.noThumbnailClass); } } Thumbnail.options = _.extend({}, ResultLink.options, Thumbnail.options); Initialization.registerAutoCreateComponent(Thumbnail);