/* eslint-disable no-underscore-dangle */
import style from './iink.css'
import { editorLogger as logger } from './configuration/LoggerConfig'
import * as DefaultBehaviors from './configuration/DefaultBehaviors'
import * as DefaultConfiguration from './configuration/DefaultConfiguration'
import * as DefaultStyles from './configuration/DefaultPenStyle'
import * as DefaultTheme from './configuration/DefaultTheme'
import * as InkModel from './model/InkModel'
import * as UndoRedoContext from './model/UndoRedoContext'
import * as UndoRedoManager from './model/UndoRedoManager'
import * as ImageRenderer from './renderer/canvas/ImageRenderer'
import * as RecognizerContext from './model/RecognizerContext'
import * as SmartGuide from './smartguide/SmartGuide'
import Constants from './configuration/Constants'
import * as eastereggs from './eastereggs/InkImporter'
import {
handleError,
handleSuccess,
emitEvents,
manageRecognizedModel
} from './recognizer/RecognizerService'
import * as PromiseHelper from './util/PromiseHelper'
/**
* Check if a clear is required, and does it if it is
* @param {Editor} editor
* @param {Model} model Current model
* @return {Promise<*>}
*/
function manageResetState (editor, model) {
// If strokes moved in the undo redo stack then a clear is mandatory before sending strokes.
if (editor.recognizer.reset && !editor.isErasing && RecognizerContext.isResetRequired(editor.recognizerContext, model)) {
return editor.recognizer.reset(editor.recognizerContext, model)
}
return null
}
/**
* Check if the trigger in parameter is valid.
* @param {Editor} editor
* @param {String} type
* @param {String} [trigger]
* @return {Boolean}
*/
function isTriggerValid (editor, type, trigger = editor.configuration.triggers[type]) {
if (editor.recognizer &&
editor.recognizer.getInfo().availableTriggers[type].includes(trigger)) {
return true
}
logger.error(`${trigger} is not a valid trigger for ${type}`)
return false
}
/**
* Launch the recognition with all editor relative configuration and state.
* @param {Editor} editor
* @param {Model} model
* @param {String} [trigger]
* @return {Promise}
*/
async function addStrokes (editor, model, trigger = editor.configuration.triggers.addStrokes) {
if (editor.recognizer && editor.recognizer.addStrokes) {
const init = await editor.recognizerContext.initPromise
if (init) {
// Firing addStrokes only if recognizer is configure to do it
if (isTriggerValid(editor, 'addStrokes', trigger)) {
const res = await manageResetState(editor, model)
if (res) {
return editor.recognizer.addStrokes(editor.recognizerContext, res)
}
return editor.recognizer.addStrokes(editor.recognizerContext, model)
}
}
}
return Promise.reject(new Error('Cannot addStrokes'))
}
/**
* Launch ink import.
* @param {Editor} editor
* @param {Model} model
* @param {PointerEvents} events
* @return {Promise<*>}
*/
async function launchPointerEvents (editor, model, events) {
if (editor.recognizer && editor.recognizer.pointerEvents) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.pointerEvents(editor.recognizerContext, model, events)
}
}
return Promise.reject(new Error('Cannot launch pointerEvents'))
}
/**
* Launch the recognition with all editor relative configuration and state.
* @param {Editor} editor
* @param {Model} model
* @param {String} [requestedMimeTypes]
* @param {String} [trigger]
*/
export async function launchExport (editor, model, requestedMimeTypes, trigger = editor.configuration.triggers.exportContent) {
if (editor.recognizer && editor.recognizer.export_) {
const init = await editor.recognizerContext.initPromise
if (init) {
if (isTriggerValid(editor, 'exportContent', trigger)) {
const editorRef = editor
window.clearTimeout(editor.exportTimer)
const timeout = trigger === Constants.Trigger.QUIET_PERIOD ? editor.configuration.triggerDelay : 0
const delayer = PromiseHelper.delay(timeout)
editorRef.exportTimer = delayer.timer
await delayer.promise
const res = await manageResetState(editor, model)
if (res) {
return editor.recognizer.export_(editor.recognizerContext, res, requestedMimeTypes)
}
return editor.recognizer.export_(editor.recognizerContext, model, requestedMimeTypes)
}
}
}
return Promise.reject(new Error('Cannot launch export'))
}
/**
* Launch the import.
* @param {Editor} editor
* @param {Model} model
* @param {Blob} data
* @return {Promise<*>}
*/
async function launchImport (editor, model, data) {
if (editor.recognizer && editor.recognizer.import_) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.import_(editor.recognizerContext, model, data)
}
}
return Promise.reject(new Error('Cannot launch import'))
}
/**
* Get the supported mimetypes for import.
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function launchGetSupportedImportMimeTypes (editor, model) {
if (editor.recognizer && editor.recognizer.getSupportedImportMimeTypes) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.getSupportedImportMimeTypes(editor.recognizerContext, model)
}
}
return Promise.reject(new Error('Cannot launch getSupportedImportMimeTypes'))
}
/**
* Launch the convert with all editor relative configuration and state.
* @param {Editor} editor
* @param {Model} model
* @param {String} conversionState
* @return {Promise<*>}
*/
async function launchConvert (editor, model, conversionState) {
if (editor.recognizer && editor.recognizer.convert) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.convert(editor.recognizerContext, model, conversionState)
}
}
return Promise.reject(new Error('Cannot launch convert'))
}
/**
* Launch the configuration for the editor
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function launchConfig (editor, model) {
if (editor.recognizer && editor.recognizer.sendConfiguration) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.sendConfiguration(editor.recognizerContext, model)
}
}
return Promise.reject(new Error('Cannot launch config'))
}
/**
* Launch the resize.
* @param {Editor} editor
* @param {Model} model
*/
async function launchResize (editor, model) {
if (editor.recognizer && editor.recognizer.resize) {
const init = await editor.recognizerContext.initPromise
if (init) {
const editorRef = editor
window.clearTimeout(editor.resizeTimer)
const delayer = PromiseHelper.delay(editor.configuration.resizeTriggerDelay)
editorRef.resizeTimer = delayer.timer
SmartGuide.resize(editor.smartGuide)
await delayer.promise
return editor.recognizer.resize(editor.recognizerContext, model, editor.domElement)
}
}
return Promise.reject(new Error('Cannot launch resize'))
}
/**
* Launch wait for idle
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function launchWaitForIdle (editor, model) {
if (editor.recognizer && editor.recognizer.waitForIdle) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.waitForIdle(editor.recognizerContext, model)
}
}
return Promise.reject(new Error('Cannot launch wait for idle'))
}
/**
* Launch websocket close
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function launchClose (editor, model) {
if (editor.smartGuide) {
SmartGuide.reset(editor.smartGuide)
}
if (editor.recognizer && editor.recognizer.close) {
const init = await editor.recognizerContext.initPromise
if (init) {
editor.loader.style.display = 'none'
editor.error.innerText = Constants.Error.CLOSE
editor.error.style.display = 'initial'
return editor.recognizer.close(editor.recognizerContext, model)
}
}
return Promise.reject(new Error('Cannot launch close'))
}
/**
* Set pen style.
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function setPenStyle (editor, model) {
if (editor.recognizer && editor.recognizer.setPenStyle) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.setPenStyle(editor.recognizerContext, model, editor.penStyle)
}
return Promise.reject(new Error('Cannot set pentStyle'))
}
return null
}
/**
* Set pen style.
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function setPenStyleClasses (editor, model) {
if (editor.recognizer && editor.recognizer.setPenStyleClasses) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.setPenStyleClasses(editor.recognizerContext, model, editor.penStyleClasses)
}
return Promise.reject(new Error('Cannot set penStyleClasses'))
}
return null
}
/**
* Set theme.
* @param {Editor} editor
* @param {Model} model
* @return {Promise<*>}
*/
async function setTheme (editor, model) {
if (editor.recognizer && editor.recognizer.setTheme) {
const init = await editor.recognizerContext.initPromise
if (init) {
return editor.recognizer.setTheme(editor.recognizerContext, model, editor.theme)
}
return Promise.reject(new Error('Cannot set theme'))
}
return null
}
/**
* Editor
*/
export class Editor {
/**
* @param {Element} element DOM element to attach this editor
* @param {Configuration} [configuration] Configuration to apply
* @param {Theme} [theme] Custom theme to apply
* @param {PenStyle} [penStyle] Custom style to apply
* @param {Behaviors} [behaviors] Custom behaviors to apply
*/
constructor (element, configuration, penStyle, theme, behaviors, globalClassCss) {
globalClassCss = globalClassCss || 'ms-editor'
const styleElement = document.createElement('style')
styleElement.appendChild(document.createTextNode(''))
element.appendChild(styleElement)
const sheet = styleElement.sheet
styleElement.textContent = style
this.sheet = sheet
/**
* Inner reference to the DOM Element
* @type {Element}
*/
this.domElement = element
this.domElement.classList.add(globalClassCss)
// eslint-disable-next-line no-undef
this.loader = document.createElement('div')
this.loader.classList.add('loader')
this.loader = this.domElement.appendChild(this.loader)
// eslint-disable-next-line no-undef
this.error = document.createElement('div')
this.error.classList.add('error-msg')
this.error = this.domElement.appendChild(this.error)
/**
* Launch export timer
* @type {Number}
*/
this.exportTimer = undefined
/**
* Launch resize timer
* @type {Number}
*/
this.resizeTimer = undefined
/**
* Notify delay timer
* @type {Number}
*/
this.notifyTimer = undefined
/**
* @private
* @type {Behaviors}
*/
this.innerBehaviors = DefaultBehaviors.overrideDefaultBehaviors(behaviors)
this.configuration = configuration
/**
* Pen color used only for pending stroke
* @type {string}
*/
this.localTheme = ''
this.theme = theme
this._setThemeFontFamily()
this.penStyle = penStyle
this.penStyleClasses = ''
// To override pointerType when ERASER
this.isErasing = false
this.domElement.editor = this
}
/**
* Set the recognition parameters
* WARNING : Need to fire a clear if user have already input some strokes.
* @param {Configuration} configuration
*/
set configuration (configuration) {
this.loader.style.display = 'initial'
this.error.style.display = 'none'
/**
* @private
* @type {Configuration}
*/
this.innerConfiguration = DefaultConfiguration.overrideDefaultConfiguration(configuration)
this.behavior = this.behaviors.getBehaviorFromConfiguration(this.behaviors, this.innerConfiguration)
if (this.smartGuide) {
SmartGuide.reset(this.smartGuide)
} else {
this.smartGuide = SmartGuide.createSmartGuide(this)
}
}
/**
* Get the current recognition parameters
* @return {Configuration}
*/
get configuration () {
return this.innerConfiguration
}
/**
* Set the pen style
* @param {PenStyle} penStyle
*/
set penStyle (penStyle) {
/**
* @private
* @type {PenStyle}
*/
this.innerPenStyle = DefaultStyles.overrideDefaultPenStyle(penStyle)
this.localPenStyle = this.innerPenStyle
setPenStyle(this, this.model)
}
/**
* Get the pen style
* @return {PenStyle}
*/
get penStyle () {
return this.innerPenStyle
}
/**
* Set the pen style
* @param {String} penStyleClasses
*/
set penStyleClasses (penStyleClasses) {
/**
* @private
* @type {String}
*/
this.innerPenStyleClasses = penStyleClasses
this.localPenStyle = this.theme[`.${this.innerPenStyleClasses}`]
setPenStyleClasses(this, this.model)
}
/**
* Get the pen style
* @return {String}
*/
get penStyleClasses () {
return this.innerPenStyleClasses
}
/**
* Set the theme
* @param {Theme} theme
*/
set theme (theme) {
/**
* @private
* @type {Theme}
*/
this.innerTheme = DefaultTheme.overrideDefaultTheme(theme)
setTheme(this, this.model)
}
/**
* Get the theme
* @return {Theme}
*/
get theme () {
return this.innerTheme
}
/**
* Get behaviors
* @return {Behaviors}
*/
get behaviors () {
return this.innerBehaviors
}
/**
* @private
* @param {Behavior} behavior
*/
set behavior (behavior) {
if (behavior) {
if (this.grabber) { // Remove event handlers to avoid multiplication (detach grabber)
this.grabber.detach(this.domElement, this.grabberContext)
}
/**
* @private
* @type {Behavior}
*/
this.innerBehavior = behavior
this.renderer = this.innerBehavior.renderer
this.recognizer = this.innerBehavior.recognizer
/**
* Current grabber context
* @type {GrabberContext}
*/
this.grabberContext = this.grabber.attach(this.domElement, this)
}
}
/**
* Get current behavior
* @return {Behavior}
*/
get behavior () {
return this.innerBehavior
}
/**
* Set the current recognizer
* @private
* @param {Recognizer} recognizer
*/
set recognizer (recognizer) {
this.undoRedoContext = UndoRedoContext.createUndoRedoContext(this.configuration)
this.undoRedoManager = UndoRedoManager
const initialize = (model, shouldSendTheme) => {
/**
* @private
* @type {Recognizer}
*/
this.innerRecognizer = recognizer
if (this.innerRecognizer) {
/**
* Current recognition context
* @type {RecognizerContext}
*/
this.recognizerContext = RecognizerContext.createEmptyRecognizerContext(this)
// FIXME: merge undo/redo manager with default recognizer
if (this.innerRecognizer.undo && this.innerRecognizer.redo && this.innerRecognizer.clear) {
this.undoRedoContext = this.recognizerContext
this.undoRedoManager = this.innerRecognizer
}
this.innerRecognizer.init(this.recognizerContext, model)
.then((values) => {
logger.info('Recognizer initialized !')
if (shouldSendTheme) {
setTheme(this, this.model)
setPenStyle(this, this.model)
setPenStyleClasses(this, this.model)
}
this.loader.style.display = 'none'
})
.catch(err => {
handleError(this, err)
})
}
}
if (recognizer) {
if (this.innerRecognizer) {
this.innerRecognizer.close(this.recognizerContext, this.model)
.then((model) => {
logger.info('Recognizer closed')
this._setThemeFontFamily()
initialize(InkModel.clearModel(model), true)
handleSuccess(this, model)
})
.catch(err => handleError(this, err))
} else {
/**
* Current model
* @type {Model}
*/
this.model = InkModel.createModel(this.configuration)
// INFO: Recognizer needs model to be initialized
initialize(this.model, false)
}
}
}
/**
* Get current recognizer
* @return {Recognizer}
*/
get recognizer () {
return this.innerRecognizer
}
/**
* Set the current renderer
* @private
* @param {Renderer} renderer
*/
set renderer (renderer) {
if (renderer) {
if (this.innerRenderer) {
this.innerRenderer.detach(this.domElement, this.rendererContext)
}
/**
* @private
* @type {Renderer}
*/
this.innerRenderer = renderer
if (this.innerRenderer) {
/**
* Current rendering context
* @type {Object}
*/
this.rendererContext = this.innerRenderer.attach(this.domElement, this.configuration.renderingParams.minHeight, this.configuration.renderingParams.minWidth)
}
}
}
/**
* Get current renderer
* @return {Renderer}
*/
get renderer () {
return this.innerRenderer
}
/**
* Get current grabber
* @return {Grabber}
*/
get grabber () {
return this.behavior ? this.behavior.grabber : undefined
}
/**
* Get current stroker
* @return {Stroker}
*/
get stroker () {
return this.behavior ? this.behavior.stroker : undefined
}
/**
* Get current events
* @return {Array}
*/
get emit () {
return this.behavior ? this.behavior.events : undefined
}
/**
* Get a PNG image data url from the data model
* @return {String}
*/
get png () {
return ImageRenderer.getImage(this.model, this.stroker)
}
/**
* True if initialized, false otherwise
* @return {Boolean}
*/
get initialized () {
return this.recognizerContext ? this.recognizerContext.initialized : false
}
enableEraser () {
this.isErasing = true
this.domElement.classList.add('erasing')
}
disableEraser () {
document.body.style.cursor = 'initial'
this.isErasing = false
this.domElement.classList.remove('erasing')
}
/**
* Handle a pointer down
* @param {{x: Number, y: Number, t: Number}} point Captured point coordinates
* @param {String} [pointerType=mouse] Current pointer type
* @param {String} [pointerId] Current pointer id
*/
pointerDown (point, pointerType = 'pen', pointerId) {
logger.trace('Pointer down', point)
window.clearTimeout(this.notifyTimer)
window.clearTimeout(this.exportTimer)
this.model = InkModel.initPendingStroke(this.model, point, Object.assign({ pointerType, pointerId }, this.theme.ink, this.localPenStyle))
this.renderer.drawCurrentStroke(this.rendererContext, this.model, this.stroker)
// Currently no recognition on pointer down
}
/**
* Handle a pointer move
* @param {{x: Number, y: Number, t: Number}} point Captured point coordinates
*/
pointerMove (point) {
logger.trace('Pointer move', point)
this.model = InkModel.appendToPendingStroke(this.model, point)
this.renderer.drawCurrentStroke(this.rendererContext, this.model, this.stroker)
// Currently no recognition on pointer move
}
/**
* Handle a pointer up
* @param {{x: Number, y: Number, t: Number}} point Captured point coordinates
*/
pointerUp (point) {
logger.trace('Pointer up', point)
this.model = InkModel.endPendingStroke(this.model, point, this.penStyle)
this.renderer.drawModel(this.rendererContext, this.model, this.stroker)
if (this.recognizer.addStrokes) {
addStrokes(this, this.model)
} else {
// Push model in undo redo manager
handleSuccess(this, this.model)
}
}
removeStroke (stroke) {
this.model.strokeGroups.forEach((group) => {
const stringStrokes = group.strokes.map(strokeFromGroup => JSON.stringify(strokeFromGroup))
const strokeIndex = stringStrokes.indexOf(JSON.stringify(stroke))
if (strokeIndex !== -1) {
group.strokes.splice(strokeIndex, 1)
}
})
const stringRawStrokes = this.model.rawStrokes.map(strokes => JSON.stringify(strokes))
const strokeIndex = stringRawStrokes.indexOf(JSON.stringify(stroke))
if (strokeIndex !== -1) {
this.model.rawStrokes.splice(strokeIndex, 1)
}
this.renderer.drawModel(this.rendererContext, this.model, this.stroker)
handleSuccess(this, this.model)
if (!(this.configuration.triggers.exportContent === 'DEMAND')) {
launchExport(this, this.model)
}
}
/**
* @Deprecated
* @param rawStrokes
* @param strokeGroups
*/
reDraw (rawStrokes, strokeGroups) {
rawStrokes.forEach((stroke) => {
InkModel.addStroke(this.model, stroke)
})
strokeGroups.forEach((group) => {
group.strokes.forEach((strokeFromGroup) => {
InkModel.addStrokeToGroup(this.model, strokeFromGroup, group.penStyle)
})
})
this.renderer.drawModel(this.rendererContext, this.model, this.stroker)
handleSuccess(this, this.model)
}
/**
* True if idle state
* @return {Boolean}
*/
get idle () {
return this.recognizerContext.idle
}
/**
* Wait for idle state.
* @return {Promise<*>}
*/
waitForIdle () {
emitEvents(this, undefined, Constants.EventType.IDLE)
return launchWaitForIdle(this, this.model)
}
/**
* True if can undo, false otherwise.
* @return {Boolean}
*/
get canUndo () {
return this.undoRedoContext.canUndo
}
/**
* Undo the last action.
* @return {Promise<*>}
*/
async undo () {
logger.debug('Undo current model', this.model)
emitEvents(this, undefined, Constants.EventType.UNDO)
const { res, types } = await this.undoRedoManager.undo(this.undoRedoContext, this.model)
.catch(err => handleError(this, err))
manageRecognizedModel(this, res, ...types)
return res
}
/**
* True if can redo, false otherwise.
* @return {Boolean}
*/
get canRedo () {
return this.undoRedoContext.canRedo
}
/**
* Redo the last action.
* @return {Promise<*>}
*/
async redo () {
logger.debug('Redo current model', this.model)
emitEvents(this, undefined, Constants.EventType.REDO)
const { res, types } = await this.undoRedoManager.redo(this.undoRedoContext, this.model)
.catch(err => handleError(this, err))
manageRecognizedModel(this, res, ...types)
return res
}
/**
* True if empty, false otherwise
* @return {boolean}
*/
get isEmpty () {
return this.recognizerContext.isEmpty
}
/**
* True if can clear, false otherwise.
* @return {Boolean}
*/
get canClear () {
return !this.isEmpty
}
/**
* Clear the output and the recognition result.
* @return {Promise<*>}
*/
async clear () {
if (this.canClear) {
logger.debug('Clear current model', this.model)
emitEvents(this, undefined, Constants.EventType.CLEAR)
const { res, events } = await this.recognizer.clear(this.recognizerContext, this.model)
.catch(error => handleError(this, error))
handleSuccess(this, res, ...events)
return res
}
return Promise.reject(new Error('Cannot launch clear'))
}
/**
* True if can convert, false otherwise.
* @return {Boolean}
*/
get canConvert () {
return !!(this.canUndo && this.canClear && this.recognizer && this.recognizer.convert)
}
/**
* Convert the current content
* @param {string} conversionState
* @return {Promise<*>}
*/
convert (conversionState = 'DIGITAL_EDIT') {
if (this.canConvert) {
emitEvents(this, undefined, Constants.EventType.CONVERT)
return launchConvert(this, this.model, conversionState)
}
return Promise.reject(new Error('Cannot launch convert'))
}
/**
* Set the guides for text
* @param {Boolean} [enable]
* @return {Promise<*|null>}
*/
setGuides (enable = true) {
this.configuration.recognitionParams.iink.text.guides.enable = enable
return launchConfig(this, this.model)
}
/**
* Return the position of the cursor identifying the current state in the internal iink undo/redo stack.
* @returns {Number}
*/
get possibleUndoCount () {
return this.recognizerContext.possibleUndoCount
}
/**
* The number of operations that it is currently possible to undo.
* @returns {Number}
*/
get undoStackIndex () {
return this.recognizerContext.undoStackIndex
}
/**
* True if can export, false otherwise.
* @return {Boolean}
*/
get canExport () {
return this.canUndo && this.canClear && this.recognizer && this.recognizer.getInfo().availableTriggers.exportContent.includes(Constants.Trigger.DEMAND)
}
/**
* Explicitly ask to perform an export. You have to listen to events to get the content as this function is non blocking and does not have a return type.
* @param {Array<String>} requestedMimeTypes Requested mime-types. Be sure to ask all the types required by the listeners of exported event.
*/
export_ (requestedMimeTypes) {
if (this.canExport) {
emitEvents(this, undefined, Constants.EventType.EXPORT)
return launchExport(this, this.model, requestedMimeTypes, Constants.Trigger.DEMAND)
}
return Promise.reject(new Error('Cannot launch export'))
}
/**
* Import content.
* @param {Blob|*} data Data to import
* @param {String} [mimetype] Mimetype of the data, needed if data is not a Blob
*/
import_ (data, mimetype) {
emitEvents(this, undefined, Constants.EventType.IMPORT)
return launchImport(this, this.model, !(data instanceof Blob) ? new Blob([data], { type: mimetype }) : data)
}
/**
* Get supported import mime types
* @return {Promise<*|null>}
*/
getSupportedImportMimeTypes () {
return launchGetSupportedImportMimeTypes(this, this.model)
}
/**
* pointer events
* @param {PointerEvents} events
* @return {Promise<*|null>}
*/
pointerEvents (events) {
return launchPointerEvents(this, this.model, events)
}
/**
* Get current state exports
* @return {Object}
*/
get exports () {
return this.model ? this.model.exports : undefined
}
get supportedImportMimeTypes () {
return this.recognizerContext.supportedImportMimeTypes
}
/**
* Function to call when the dom element link to the current ink paper has been resize.
*/
resize () {
logger.debug('Resizing editor')
this.renderer.resize(this.rendererContext, this.model, this.stroker, this.configuration.renderingParams.minHeight, this.configuration.renderingParams.minWidth)
return launchResize(this, this.model)
}
/**
* Detach event listeners from the DOM element created at editor creation.
*/
unload () {
if (this.grabber) { // Remove event handlers to avoid multiplication (detach grabber)
this.grabber.detach(this.domElement, this.grabberContext)
}
if (this.innerRenderer) {
this.innerRenderer.detach(this.domElement, this.rendererContext)
}
}
/**
* Close websocket connection
* @return {Promise<*>}
*/
close () {
if (this.configuration.recognitionParams.protocol === Constants.Protocol.WEBSOCKET) {
return launchClose(this, this.model)
}
return null
}
/**
* Apply the font-family on theem corresponding to chosen language
* @private
*/
_setThemeFontFamily () {
const defaultLang = !Object.keys(Constants.Languages).includes(this.configuration.recognitionParams.iink.lang)
this.theme['.text']['font-family'] = defaultLang ? Constants.Languages.default : Constants.Languages[this.configuration.recognitionParams.iink.lang]
}
/**
* Trigger the change callbacks (and by default send a change event).
*/
forceChange () {
emitEvents(this, undefined, Constants.EventType.CHANGED)
}
/* eslint-disable class-methods-use-this */
/**
* Get access to some easter egg features link ink injection. Use at your own risk (less tested and may be removed without notice).
*/
get eastereggs () {
return eastereggs
}
/* eslint-enable class-methods-use-this */
}