coveo-search-ui
Version:
Coveo JavaScript Search Framework
173 lines (149 loc) • 6.23 kB
text/typescript
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);