SignatureTabletWidgets.js

/**
* @module SignatureTabletWidgets
*/

import { blobToBase64, checkResponse } from './Common.js'

/**
 * DisplayWidget
 * @classdesc Represents a widget that can be displayed at some x and y coordinates
 * @see {@link TextButton}
 * @see {@link Text}
 * @see {@link Image}
 */
export class DisplayWidget {
  x
  y
  constructor (x, y) {
    this.x = x
    this.y = y
  }
}

/**
 * TextButton
 * @classdesc A button for displaying clickable text on a tablet
 * @extends DisplayWidget
 * @see {@link SignatureTablet}
 */
export class TextButton extends DisplayWidget {
  text
  fontFace
  fontSize
  id
  /**
   * Instantiate a Tablet Text Button
   * @constructor
   * @param {string} text - Text to display on the button
   * @param {number} x - X position to draw the button
   * @param {number} y - Y position to draw the button
   * @param {text} [id] - ID of the button that will be returned when it is
   * clicked. If not set one will attempt to be constructed from the button
   * text but there is no guarantee it will be unique.
   * @param {string} [fontFace] - Font face for the text on the button.
   * Defaults to 'Arial'
   * @param {number} [fontSize] - Size of the font for the text on the button.
   * Defaults to 40.
   * @example
   * const new TextButton("OK", 20, 200)
   */
  constructor (text, x, y, id, fontFace = 'Arial', fontSize = 40) {
    super(x, y)
    this.text = text
    this.fontFace = fontFace
    this.fontSize = fontSize
    if (id) {
      this.id = id
    } else {
      this.id = text.length > 16 ? text.slice(0, 16) : text
    }
  }

  toJSON () {
    return {
      type: 'button',
      text: this.text,
      font_face: this.fontFace,
      font_size: this.fontSize,
      x: this.x,
      y: this.y,
      id: this.id
    }
  }
}

/**
 * Text
 * @classdesc Text to be displayed on a tablet
 * @extends DisplayWidget
 * @see {@link SignatureTablet}
 */
export class Text extends DisplayWidget {
  text
  fontFace
  fontSize
  /**
   * Instantiate a Text object
   * @constructor
   * @param {string} text - Text to display on the button
   * @param {number} x - X position to draw the button
   * @param {number} y - Y position to draw the button
   * @param {string} [fontFace] - Font face for the text on the button.
   * Defaults to 'Arial'
   * @param {number} [fontSize] - Size of the font for the text on the button.
   * Defaults to 40.
   * @example
   * const new Text("Do you agree?", 20, 200)
   */
  constructor (text, x, y, fontFace = 'Arial', fontSize = 40) {
    super(x, y)
    this.text = text
    this.fontFace = fontFace
    this.fontSize = fontSize
  }

  toJSON () {
    return {
      type: 'text',
      text: this.text,
      font_face: this.fontFace,
      font_size: this.fontSize,
      x: this.x,
      y: this.y
    }
  }
}

/**
 * Image
 * @classdesc Image to be displayed on a tablet
 * @extends DisplayWidget
 * @see {@link SignatureTablet}
 */
export class Image extends DisplayWidget {
  url
  imageData
  delay
  /**
   * Instantiate an Image object
   * @constructor
   * @param {string} url - URL of the image.
   * @param {number} [x] - X position to draw the button
   * @param {number} [y] - Y position to draw the button
   * @param {number} [delay] - Milliseconds to sleep after displaying the image
   * @example
   * const new Image("/img/logo.tif", 20, 200)
   */
  constructor (url, x = 0, y = 0, delay = 0) {
    super(x, y)
    this.url = url
    this.delay = delay
  }

  /**
   * Fetch the image data and base64 encode it
   * @async
   */
  async data () {
    if (!this.imageData && this.url) {
      const response = await fetch(this.url)
      await checkResponse(response)
      const blob = await response.blob()
      this.imageData = await blobToBase64(blob)
    }
    return this.imageData
  }

  toJSON () {
    return {
      type: 'image',
      data: this.imageData,
      delay: this.delay,
      x: this.x,
      y: this.y
    }
  }
}