175 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import extend from 'extend';
 | 
						|
import Delta from 'quill-delta';
 | 
						|
import Parchment from 'parchment';
 | 
						|
import Break from './break';
 | 
						|
import Inline from './inline';
 | 
						|
import TextBlot from './text';
 | 
						|
 | 
						|
 | 
						|
const NEWLINE_LENGTH = 1;
 | 
						|
 | 
						|
 | 
						|
class BlockEmbed extends Parchment.Embed {
 | 
						|
  attach() {
 | 
						|
    super.attach();
 | 
						|
    this.attributes = new Parchment.Attributor.Store(this.domNode);
 | 
						|
  }
 | 
						|
 | 
						|
  delta() {
 | 
						|
    return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values()));
 | 
						|
  }
 | 
						|
 | 
						|
  format(name, value) {
 | 
						|
    let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE);
 | 
						|
    if (attribute != null) {
 | 
						|
      this.attributes.attribute(attribute, value);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  formatAt(index, length, name, value) {
 | 
						|
    this.format(name, value);
 | 
						|
  }
 | 
						|
 | 
						|
  insertAt(index, value, def) {
 | 
						|
    if (typeof value === 'string' && value.endsWith('\n')) {
 | 
						|
      let block = Parchment.create(Block.blotName);
 | 
						|
      this.parent.insertBefore(block, index === 0 ? this : this.next);
 | 
						|
      block.insertAt(0, value.slice(0, -1));
 | 
						|
    } else {
 | 
						|
      super.insertAt(index, value, def);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT;
 | 
						|
// It is important for cursor behavior BlockEmbeds use tags that are block level elements
 | 
						|
 | 
						|
 | 
						|
class Block extends Parchment.Block {
 | 
						|
  constructor(domNode) {
 | 
						|
    super(domNode);
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  delta() {
 | 
						|
    if (this.cache.delta == null) {
 | 
						|
      this.cache.delta = this.descendants(Parchment.Leaf).reduce((delta, leaf) => {
 | 
						|
        if (leaf.length() === 0) {
 | 
						|
          return delta;
 | 
						|
        } else {
 | 
						|
          return delta.insert(leaf.value(), bubbleFormats(leaf));
 | 
						|
        }
 | 
						|
      }, new Delta()).insert('\n', bubbleFormats(this));
 | 
						|
    }
 | 
						|
    return this.cache.delta;
 | 
						|
  }
 | 
						|
 | 
						|
  deleteAt(index, length) {
 | 
						|
    super.deleteAt(index, length);
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  formatAt(index, length, name, value) {
 | 
						|
    if (length <= 0) return;
 | 
						|
    if (Parchment.query(name, Parchment.Scope.BLOCK)) {
 | 
						|
      if (index + length === this.length()) {
 | 
						|
        this.format(name, value);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);
 | 
						|
    }
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  insertAt(index, value, def) {
 | 
						|
    if (def != null) return super.insertAt(index, value, def);
 | 
						|
    if (value.length === 0) return;
 | 
						|
    let lines = value.split('\n');
 | 
						|
    let text = lines.shift();
 | 
						|
    if (text.length > 0) {
 | 
						|
      if (index < this.length() - 1 || this.children.tail == null) {
 | 
						|
        super.insertAt(Math.min(index, this.length() - 1), text);
 | 
						|
      } else {
 | 
						|
        this.children.tail.insertAt(this.children.tail.length(), text);
 | 
						|
      }
 | 
						|
      this.cache = {};
 | 
						|
    }
 | 
						|
    let block = this;
 | 
						|
    lines.reduce(function(index, line) {
 | 
						|
      block = block.split(index, true);
 | 
						|
      block.insertAt(0, line);
 | 
						|
      return line.length;
 | 
						|
    }, index + text.length);
 | 
						|
  }
 | 
						|
 | 
						|
  insertBefore(blot, ref) {
 | 
						|
    let head = this.children.head;
 | 
						|
    super.insertBefore(blot, ref);
 | 
						|
    if (head instanceof Break) {
 | 
						|
      head.remove();
 | 
						|
    }
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  length() {
 | 
						|
    if (this.cache.length == null) {
 | 
						|
      this.cache.length = super.length() + NEWLINE_LENGTH;
 | 
						|
    }
 | 
						|
    return this.cache.length;
 | 
						|
  }
 | 
						|
 | 
						|
  moveChildren(target, ref) {
 | 
						|
    super.moveChildren(target, ref);
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  optimize(context) {
 | 
						|
    super.optimize(context);
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  path(index) {
 | 
						|
    return super.path(index, true);
 | 
						|
  }
 | 
						|
 | 
						|
  removeChild(child) {
 | 
						|
    super.removeChild(child);
 | 
						|
    this.cache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  split(index, force = false) {
 | 
						|
    if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {
 | 
						|
      let clone = this.clone();
 | 
						|
      if (index === 0) {
 | 
						|
        this.parent.insertBefore(clone, this);
 | 
						|
        return this;
 | 
						|
      } else {
 | 
						|
        this.parent.insertBefore(clone, this.next);
 | 
						|
        return clone;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      let next = super.split(index, force);
 | 
						|
      this.cache = {};
 | 
						|
      return next;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
Block.blotName = 'block';
 | 
						|
Block.tagName = 'P';
 | 
						|
Block.defaultChild = 'break';
 | 
						|
Block.allowedChildren = [Inline, Parchment.Embed, TextBlot];
 | 
						|
 | 
						|
 | 
						|
function bubbleFormats(blot, formats = {}) {
 | 
						|
  if (blot == null) return formats;
 | 
						|
  if (typeof blot.formats === 'function') {
 | 
						|
    formats = extend(formats, blot.formats());
 | 
						|
  }
 | 
						|
  if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {
 | 
						|
    return formats;
 | 
						|
  }
 | 
						|
  return bubbleFormats(blot.parent, formats);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
export { bubbleFormats, BlockEmbed, Block as default };
 |