tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mark_step.ts (7357B)


      1 import {Fragment, Slice, Node, Mark, Schema} from "prosemirror-model"
      2 import {Step, StepResult} from "./step"
      3 import {Mappable} from "./map"
      4 
      5 function mapFragment(fragment: Fragment, f: (child: Node, parent: Node, i: number) => Node, parent: Node): Fragment {
      6  let mapped = []
      7  for (let i = 0; i < fragment.childCount; i++) {
      8    let child = fragment.child(i)
      9    if (child.content.size) child = child.copy(mapFragment(child.content, f, child))
     10    if (child.isInline) child = f(child, parent, i)
     11    mapped.push(child)
     12  }
     13  return Fragment.fromArray(mapped)
     14 }
     15 
     16 /// Add a mark to all inline content between two positions.
     17 export class AddMarkStep extends Step {
     18  /// Create a mark step.
     19  constructor(
     20    /// The start of the marked range.
     21    readonly from: number,
     22    /// The end of the marked range.
     23    readonly to: number,
     24    /// The mark to add.
     25    readonly mark: Mark
     26  ) {
     27    super()
     28  }
     29 
     30  apply(doc: Node) {
     31    let oldSlice = doc.slice(this.from, this.to), $from = doc.resolve(this.from)
     32    let parent = $from.node($from.sharedDepth(this.to))
     33    let slice = new Slice(mapFragment(oldSlice.content, (node, parent) => {
     34      if (!node.isAtom || !parent.type.allowsMarkType(this.mark.type)) return node
     35      return node.mark(this.mark.addToSet(node.marks))
     36    }, parent), oldSlice.openStart, oldSlice.openEnd)
     37    return StepResult.fromReplace(doc, this.from, this.to, slice)
     38  }
     39 
     40  invert(): Step {
     41    return new RemoveMarkStep(this.from, this.to, this.mark)
     42  }
     43 
     44  map(mapping: Mappable): Step | null {
     45    let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
     46    if (from.deleted && to.deleted || from.pos >= to.pos) return null
     47    return new AddMarkStep(from.pos, to.pos, this.mark)
     48  }
     49 
     50  merge(other: Step): Step | null {
     51    if (other instanceof AddMarkStep &&
     52        other.mark.eq(this.mark) &&
     53        this.from <= other.to && this.to >= other.from)
     54      return new AddMarkStep(Math.min(this.from, other.from),
     55                             Math.max(this.to, other.to), this.mark)
     56    return null
     57  }
     58 
     59  toJSON(): any {
     60    return {stepType: "addMark", mark: this.mark.toJSON(),
     61            from: this.from, to: this.to}
     62  }
     63 
     64  /// @internal
     65  static fromJSON(schema: Schema, json: any) {
     66    if (typeof json.from != "number" || typeof json.to != "number")
     67      throw new RangeError("Invalid input for AddMarkStep.fromJSON")
     68    return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
     69  }
     70 }
     71 
     72 Step.jsonID("addMark", AddMarkStep)
     73 
     74 /// Remove a mark from all inline content between two positions.
     75 export class RemoveMarkStep extends Step {
     76  /// Create a mark-removing step.
     77  constructor(
     78    /// The start of the unmarked range.
     79    readonly from: number,
     80    /// The end of the unmarked range.
     81    readonly to: number,
     82    /// The mark to remove.
     83    readonly mark: Mark
     84  ) {
     85    super()
     86  }
     87 
     88  apply(doc: Node) {
     89    let oldSlice = doc.slice(this.from, this.to)
     90    let slice = new Slice(mapFragment(oldSlice.content, node => {
     91      return node.mark(this.mark.removeFromSet(node.marks))
     92    }, doc), oldSlice.openStart, oldSlice.openEnd)
     93    return StepResult.fromReplace(doc, this.from, this.to, slice)
     94  }
     95 
     96  invert(): Step {
     97    return new AddMarkStep(this.from, this.to, this.mark)
     98  }
     99 
    100  map(mapping: Mappable): Step | null {
    101    let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
    102    if (from.deleted && to.deleted || from.pos >= to.pos) return null
    103    return new RemoveMarkStep(from.pos, to.pos, this.mark)
    104  }
    105 
    106  merge(other: Step): Step | null {
    107    if (other instanceof RemoveMarkStep &&
    108        other.mark.eq(this.mark) &&
    109        this.from <= other.to && this.to >= other.from)
    110      return new RemoveMarkStep(Math.min(this.from, other.from),
    111                                Math.max(this.to, other.to), this.mark)
    112    return null
    113  }
    114 
    115  toJSON(): any {
    116    return {stepType: "removeMark", mark: this.mark.toJSON(),
    117            from: this.from, to: this.to}
    118  }
    119 
    120  /// @internal
    121  static fromJSON(schema: Schema, json: any) {
    122    if (typeof json.from != "number" || typeof json.to != "number")
    123      throw new RangeError("Invalid input for RemoveMarkStep.fromJSON")
    124    return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
    125  }
    126 }
    127 
    128 Step.jsonID("removeMark", RemoveMarkStep)
    129 
    130 /// Add a mark to a specific node.
    131 export class AddNodeMarkStep extends Step {
    132  /// Create a node mark step.
    133  constructor(
    134    /// The position of the target node.
    135    readonly pos: number,
    136    /// The mark to add.
    137    readonly mark: Mark
    138  ) {
    139    super()
    140  }
    141 
    142  apply(doc: Node) {
    143    let node = doc.nodeAt(this.pos)
    144    if (!node) return StepResult.fail("No node at mark step's position")
    145    let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks))
    146    return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1))
    147  }
    148 
    149  invert(doc: Node): Step {
    150    let node = doc.nodeAt(this.pos)
    151    if (node) {
    152      let newSet = this.mark.addToSet(node.marks)
    153      if (newSet.length == node.marks.length) {
    154        for (let i = 0; i < node.marks.length; i++)
    155          if (!node.marks[i].isInSet(newSet))
    156            return new AddNodeMarkStep(this.pos, node.marks[i])
    157        return new AddNodeMarkStep(this.pos, this.mark)
    158      }
    159    }
    160    return new RemoveNodeMarkStep(this.pos, this.mark)
    161  }
    162 
    163  map(mapping: Mappable): Step | null {
    164    let pos = mapping.mapResult(this.pos, 1)
    165    return pos.deletedAfter ? null : new AddNodeMarkStep(pos.pos, this.mark)
    166  }
    167 
    168  toJSON(): any {
    169    return {stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON()}
    170  }
    171 
    172  /// @internal
    173  static fromJSON(schema: Schema, json: any) {
    174    if (typeof json.pos != "number")
    175      throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON")
    176    return new AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark))
    177  }
    178 }
    179 
    180 Step.jsonID("addNodeMark", AddNodeMarkStep)
    181 
    182 /// Remove a mark from a specific node.
    183 export class RemoveNodeMarkStep extends Step {
    184  /// Create a mark-removing step.
    185  constructor(
    186    /// The position of the target node.
    187    readonly pos: number,
    188    /// The mark to remove.
    189    readonly mark: Mark
    190  ) {
    191    super()
    192  }
    193 
    194  apply(doc: Node) {
    195    let node = doc.nodeAt(this.pos)
    196    if (!node) return StepResult.fail("No node at mark step's position")
    197    let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks))
    198    return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1))
    199  }
    200 
    201  invert(doc: Node): Step {
    202    let node = doc.nodeAt(this.pos)
    203    if (!node || !this.mark.isInSet(node.marks)) return this
    204    return new AddNodeMarkStep(this.pos, this.mark)
    205  }
    206 
    207  map(mapping: Mappable): Step | null {
    208    let pos = mapping.mapResult(this.pos, 1)
    209    return pos.deletedAfter ? null : new RemoveNodeMarkStep(pos.pos, this.mark)
    210  }
    211 
    212  toJSON(): any {
    213    return {stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON()}
    214  }
    215 
    216  /// @internal
    217  static fromJSON(schema: Schema, json: any) {
    218    if (typeof json.pos != "number")
    219      throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON")
    220    return new RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark))
    221  }
    222 }
    223 
    224 Step.jsonID("removeNodeMark", RemoveNodeMarkStep)