Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.atlassian.prosemirror.collab
import com.atlassian.prosemirror.state.EditorStateConfig
import com.atlassian.prosemirror.state.PMEditorState
import com.atlassian.prosemirror.state.Plugin
import com.atlassian.prosemirror.state.PluginKey
import com.atlassian.prosemirror.state.PluginSpec
import com.atlassian.prosemirror.state.StateField
import com.atlassian.prosemirror.state.TextSelection
import com.atlassian.prosemirror.state.Transaction
import com.atlassian.prosemirror.transform.Step
import com.atlassian.prosemirror.transform.Transform
import kotlin.random.Random
data class Rebaseable(
val step: Step,
val inverted: Step,
val origin: Transform
)
/** Undo a given set of steps, apply a set of other steps, and then
* redo them @internal
*/
fun rebaseSteps(steps: List, over: List, transform: Transform): List {
for (i in steps.size - 1 downTo 0) transform.step(steps[i].inverted)
for (step in over) transform.step(step)
val result = mutableListOf()
var mapFrom = steps.size
steps.forEachIndexed { i, step ->
val mapped = step.step.map(transform.mapping.slice(mapFrom))
mapFrom--
if (mapped != null && transform.maybeStep(mapped).failed == null) {
transform.mapping.setMirror(mapFrom, transform.steps.size - 1)
result.add(Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.size - 1]), steps[i].origin))
}
}
return result
}
/**
* This state field accumulates changes that have to be sent to the
* central authority in the collaborating group and makes it possible
* to integrate changes made by peers into our local document. It is
* defined by the plugin, and will be available as the `collab` field
* in the resulting editor state.
*/
data class CollabState(
/**
* The version number of the last update received from the central
* authority. Starts at 0 or the value of the `version` property
* in the option object, for the editor's value when the option
* was enabled.
*/
val version: Int,
/**
* The local steps that havent been successfully sent to the
* server yet.
*/
val unconfirmed: List
) {
constructor(config: CollabConfig) : this(config.version, emptyList())
}
fun unconfirmedFrom(transform: Transform): List {
val result = mutableListOf()
transform.steps.forEachIndexed { index, step ->
result.add(
Rebaseable(step, step.invert(transform.docs[index]), transform)
)
}
return result
}
val collabKey = PluginKey("collab")
data class CollabConfig(
/**
* The starting version number of the collaborative editing.
* Defaults to 0.
*/
val version: Int = 0,
/**
* This client's ID, used to distinguish its changes from those of
* other clients. Defaults to a random 32-bit number.
*/
val clientID: String?
)
/**
* Creates a plugin that enables the collaborative editing framework
* for the editor.
*/
fun collab(config: CollabConfig = CollabConfig(0, null)): CollabPlugin {
return CollabPlugin(config)
}
class CollabPlugin(
override val spec: CollabPluginSpec
) : Plugin(spec) {
constructor(config: CollabConfig) : this(
CollabPluginSpec(
CollabConfig(
version = config.version,
clientID = config.clientID ?: Random.nextInt(0, Integer.MAX_VALUE).toString()
)
)
)
override fun toString(): String {
return "CollabPlugin: ${spec.config}"
}
}
class CollabPluginSpec(conf: CollabConfig) : PluginSpec() {
override val key = collabKey
override val state: StateField = object : StateField {
override fun init(config: EditorStateConfig, instance: PMEditorState): CollabState {
return CollabState(conf.version, emptyList())
}
override fun apply(
tr: Transaction,
value: CollabState,
oldState: PMEditorState,
newState: PMEditorState
): CollabState {
val state = tr.getMeta(collabKey)
return when {
state != null -> state as CollabState
tr.docChanged -> CollabState(value.version, value.unconfirmed + unconfirmedFrom(tr))
else -> value
}
}
}
override val additionalProps: Map = mapOf(
"config" to conf,
// This is used to notify the history plugin to not merge steps,
// so that the history can be rebased.
"historyPreserveItems" to true
)
val config: CollabConfig by additionalProps
val historyPreserveItems: Boolean by additionalProps
}
/**
* Create a transaction that represents a set of new steps received from
* the authority. Applying this transaction moves the state forward to
* adjust to the authority's view of the document.
*/
fun receiveTransaction(
state: PMEditorState,
steps: List,
clientIDs: List,
// options: {
/**
* When enabled (the default is `false`), if the current
* selection is a [text selection](#state.TextSelection), its
* sides are mapped with a negative bias for this transaction, so
* that content inserted at the cursor ends up after the cursor.
* Users usually prefer this, but it isn't done by default for
* reasons of backwards compatibility.
*/
mapSelectionBackward: Boolean = false
// } = {}
): Transaction {
var steps = steps
// Pushes a set of steps (received from the central authority) into
// the editor state (which should have the collab plugin enabled).
// Will recognize its own changes, and confirm unconfirmed steps as
// appropriate. Remaining unconfirmed steps will be rebased over
// remote steps.
val collabState = collabKey.getState(state)!!
val version = collabState.version + steps.size
val collabPlugin = collabKey.get(state) as CollabPlugin
val ourID: String? = collabPlugin.spec.config.clientID
// Find out which prefix of the steps originated with us
var ours = 0
while (ours < clientIDs.size && clientIDs[ours] == ourID) ++ours
var unconfirmed = collabState.unconfirmed.drop(ours)
steps = if (ours != 0) steps.drop(ours) else steps
// If all steps originated with us, we're done.
if (steps.isEmpty()) {
return state.tr.setMeta(collabKey, CollabState(version, unconfirmed))
}
val nUnconfirmed = unconfirmed.size
val tr = state.tr
unconfirmed = if (nUnconfirmed != 0) {
rebaseSteps(unconfirmed, steps, tr)
} else {
for (step in steps) {
tr.step(step)
}
emptyList()
}
val newCollabState = CollabState(version, unconfirmed)
if (mapSelectionBackward && state.selection is TextSelection) {
tr.setSelection(
TextSelection.between(
tr.doc.resolve(tr.mapping.map(state.selection.anchor, -1)),
tr.doc.resolve(tr.mapping.map(state.selection.head, -1)),
bias = -1
)
)
// ;(tr as any).updated &= ~1 #TODO Aleksei - what is this line about?
}
return tr.setMeta("rebased", nUnconfirmed).setMeta("addToHistory", false).setMeta(collabKey, newCollabState)
}
/**
* Provides data describing the editor's unconfirmed steps, which need
* to be sent to the central authority. Returns null when there is
* nothing to send.
* `origins` holds the _original_ transactions that produced each
* steps. This can be useful for looking up time stamps and other
* metadata for the steps, but note that the steps may have been
* rebased, whereas the origin transactions are still the old,
* unchanged objects.
*/
fun sendableSteps(state: PMEditorState): SendableSteps? {
val collabState = collabKey.getState(state) as CollabState
if (collabState.unconfirmed.isEmpty()) return null
return SendableSteps(
version = collabState.version,
steps = collabState.unconfirmed.map { it.step },
clientID = (collabKey.get(state) as CollabPlugin).spec.config.clientID,
// TODO Aleksei - what is going on here?
origins = emptyList()
// get origins() {
// return (this as any)._origins || ((this as any)._origins = collabState.unconfirmed.map{ s -> s.origin})
// }
)
}
data class SendableSteps(
val version: Int,
val steps: List,
val clientID: String?,
val origins: List
)
/**
* Get the version up to which the collab plugin has synced with the central authority.
*/
fun getVersion(state: PMEditorState): Int {
return collabKey.getState(state)!!.version
}