soocrate-core
Version:
this is the core of soocrate application
295 lines (253 loc) • 7.78 kB
JavaScript
import { EventEmitter } from 'events'
import LSEQTree from 'lseqtree'
import { Communication } from './communication/Communication'
import Marker from './view/Marker'
var debug = require('debug')('CRATE:Document')
export default class Document extends EventEmitter {
constructor(options, documentIndex, crate) {
super()
this._options = options
this.documentIndex = documentIndex
this.crate = crate
this.documentId = this._options.signalingOptions.session
this._lastChanges = new Date().getTime()
this.documentActiveWatcher
this.setState('active')
this.name = options.name
//User ID
this.user = options.user
this.uid = this.user.id
this.lastSentMsgId = null
this.sleepingMonitor = false
}
async initView() {
if (this._options.display) {
const {
View
} = await import(/* webpackMode: "eager", webpackChunkName: "Crate-View" */ './view/View.js')
this._view = new View(this._options, this, this._options.containerID)
}
}
/**
* connect to the session of the document
*/
async init() {
let options = this._options
this._communication = new Communication({
document: this,
...this._options
})
try {
await this._communication.initConnection()
} catch (err) {
throw new Error('Could not establish connection!' + err)
}
this.sequence = new LSEQTree(
this._communication._data_comm.broadcast._causality.local.e
)
this.delta = { ops: [] }
/* TODO:Think about the creation of modules without view */
await this._communication.initModules()
// #1B if it is imported from an existing object, initialize it with these
// #2 grant fast access
this._foglet = this._communication._foglet
this.broadcast = this._communication._data_comm.broadcast
this.broadcastCaret = this._communication._behaviors_comm.broadcast
this.rps = this._communication._data_comm.network.rps
this.signalingOptions = options.signalingOptions
if (options.importFromJSON) {
await this.loadFromJSON(options.importFromJSON)
}
this.causality = this.broadcast._causality
if (options.display) {
await this._view.init()
}
this.emit('connected')
}
setMessageState(msg) {
if (this._options.display) {
this._view.setMessageState(msg)
}
}
/**
* setLastChangesTime set the last time of changes
*/
setLastChangesTime() {
this._lastChanges = new Date().getTime()
this.refreshDocument(this.sequence)
this.emit('changed')
this.activityDetected()
}
activityDetected() {
if (this.sleepingMonitor) {
if (this.state === 'sleep') {
//TODO:wake up the server if it exists
}
this.setState('active')
this.resetSleepTimer()
}
}
setState(state) {
this.state = state
this.emit('stateChanged', state)
}
startSleepMonitor() {
this.sleepingMonitor = true
this.setState('active')
this.resetSleepTimer()
}
stopSleepMonitor(timeout = null) {
if (timeout) this._options.documentActivityTimeout = timeout
clearTimeout(this.documentActivityWatcher)
this.setState('active')
this.sleepingMonitor = false
}
/**
* Reset the sleep timer
*/
resetSleepTimer() {
clearTimeout(this.documentActivityWatcher)
this.documentActivityWatcher = setTimeout(() => {
this.setState('sleep')
}, this._options.documentActivityTimeout)
}
/*!
* \brief create the core from an existing object
* \param object the object to initialize the core model of crate containing a
* sequence and causality tracking metadata
*/
async loadFromJSON(object) {
this.broadcast._causality = this.broadcast._causality.constructor.fromJSON(
object.causality
)
const local = this.broadcast._causality.local
this._communication._behaviors_comm.broadcast._causality.local.e = local.e
this.sequence.fromJSON(object.sequence)
this.sequence._s = local.e
this.sequence._c = local.v
this.getDeltaFromSequence()
.then(delta => {
this.delta = delta
})
.catch(err => {
console.error(err)
})
}
/**
* saveDocument save the document in local storage
* @return {[type]} [description]
*/
saveDocument() {
try {
const timeNow = new Date().getTime()
const document = {
date: timeNow,
title: this.name,
delta: this._view._editor.viewEditor.editor.delta,
sequence: this.sequence,
causality: this.causality,
name: this.name,
webRTCOptions: this.webRTCOptions,
markers: {},
signalingOptions: this.signalingOptions
}
store.set('CRATE2-' + this.signalingOptions.session, document)
debug('Document saved => ', document)
return true
} catch (error) {
console.error(error)
return false
}
}
refreshDocument(sequence, WhoWriteIt = false) {
if (this._options.display) {
clearTimeout(this.refreshDocumentTimeout)
this.refreshDocumentTimeout = setTimeout(() => {
let range = this._view._editor.viewEditor.getSelection()
this._view._editor.viewEditor.setContents(this.getDelta(), 'silent')
this._view._editor.viewEditor.setSelection(range, 'silent')
this._view._editor.updateCommentsLinks()
this.getDeltaFromSequence().then(delta => {
this.delta = delta
let range = this._view._editor.viewEditor.getSelection()
this._view._editor.viewEditor.setContents(this.getDelta(), 'silent')
this._view._editor.viewEditor.setSelection(range, 'silent')
this._view._editor.updateCommentsLinks()
})
}, 10)
}
}
getDelta(delta = this.delta) {
let ops = delta.ops.slice(0)
ops.push({ insert: '\n' })
return { ops }
}
getDeltaFromSequence(WhoWriteIt = false) {
return new Promise((resolve, reject) => {
let LSEQNodes = this.getLSEQNodes()
let ops = []
LSEQNodes.forEach(node => {
let op = { insert: node.e.content, attributes: node.e.attributes }
if (WhoWriteIt) {
const id = node.t.s
op.attributes.color = Marker.getColor(id)
}
ops.push(op)
})
const delta = { ops }
resolve(delta)
})
}
getLSEQNodes() {
let LSEQNodeArray = []
const root = this.sequence.root
let preorder = node => {
if (node.e && node.e != '') {
LSEQNodeArray.push(node)
}
const children = node.children
children.forEach(child => {
preorder(child)
})
}
preorder(root)
return LSEQNodeArray
}
reload() {
this._communication.close()
if (this._view) {
this._view.close()
}
this.init()
}
close() {
this._communication.close()
clearTimeout(this.documentActivityWatcher)
clearTimeout(this.refreshDocumentTimeout)
if (this._view) {
this._view.close()
}
this.crate.removeDocument(this.documentIndex)
}
/**
* get the id of the session
*/
getId(options) {
return options.signalingOptions.session
}
/**
* focus on the next session
*/
moveToNext() {
this.crate.moveToNext(this.documentId)
}
/**
* focus on the previous session
*/
moveToPrevious() {
this.crate.moveToPrevious(this.documentId)
}
createNewDocument(documentId) {
this.crate.createNewDocument(documentId)
}
}