You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					359 lines
				
				12 KiB
			
		
		
			
		
	
	
					359 lines
				
				12 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								import extend from 'extend';
							 | 
						||
| 
								 | 
							
								import Delta from 'quill-delta';
							 | 
						||
| 
								 | 
							
								import Parchment from 'parchment';
							 | 
						||
| 
								 | 
							
								import Quill from '../core/quill';
							 | 
						||
| 
								 | 
							
								import logger from '../core/logger';
							 | 
						||
| 
								 | 
							
								import Module from '../core/module';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import { AlignAttribute, AlignStyle } from '../formats/align';
							 | 
						||
| 
								 | 
							
								import { BackgroundStyle } from '../formats/background';
							 | 
						||
| 
								 | 
							
								import CodeBlock from '../formats/code';
							 | 
						||
| 
								 | 
							
								import { ColorStyle } from '../formats/color';
							 | 
						||
| 
								 | 
							
								import { DirectionAttribute, DirectionStyle } from '../formats/direction';
							 | 
						||
| 
								 | 
							
								import { FontStyle } from '../formats/font';
							 | 
						||
| 
								 | 
							
								import { SizeStyle } from '../formats/size';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let debug = logger('quill:clipboard');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const DOM_KEY = '__ql-matcher';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const CLIPBOARD_CONFIG = [
							 | 
						||
| 
								 | 
							
								  [Node.TEXT_NODE, matchText],
							 | 
						||
| 
								 | 
							
								  [Node.TEXT_NODE, matchNewline],
							 | 
						||
| 
								 | 
							
								  ['br', matchBreak],
							 | 
						||
| 
								 | 
							
								  [Node.ELEMENT_NODE, matchNewline],
							 | 
						||
| 
								 | 
							
								  [Node.ELEMENT_NODE, matchBlot],
							 | 
						||
| 
								 | 
							
								  [Node.ELEMENT_NODE, matchSpacing],
							 | 
						||
| 
								 | 
							
								  [Node.ELEMENT_NODE, matchAttributor],
							 | 
						||
| 
								 | 
							
								  [Node.ELEMENT_NODE, matchStyles],
							 | 
						||
| 
								 | 
							
								  ['li', matchIndent],
							 | 
						||
| 
								 | 
							
								  ['b', matchAlias.bind(matchAlias, 'bold')],
							 | 
						||
| 
								 | 
							
								  ['i', matchAlias.bind(matchAlias, 'italic')],
							 | 
						||
| 
								 | 
							
								  ['style', matchIgnore]
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const ATTRIBUTE_ATTRIBUTORS = [
							 | 
						||
| 
								 | 
							
								  AlignAttribute,
							 | 
						||
| 
								 | 
							
								  DirectionAttribute
							 | 
						||
| 
								 | 
							
								].reduce(function(memo, attr) {
							 | 
						||
| 
								 | 
							
								  memo[attr.keyName] = attr;
							 | 
						||
| 
								 | 
							
								  return memo;
							 | 
						||
| 
								 | 
							
								}, {});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const STYLE_ATTRIBUTORS = [
							 | 
						||
| 
								 | 
							
								  AlignStyle,
							 | 
						||
| 
								 | 
							
								  BackgroundStyle,
							 | 
						||
| 
								 | 
							
								  ColorStyle,
							 | 
						||
| 
								 | 
							
								  DirectionStyle,
							 | 
						||
| 
								 | 
							
								  FontStyle,
							 | 
						||
| 
								 | 
							
								  SizeStyle
							 | 
						||
| 
								 | 
							
								].reduce(function(memo, attr) {
							 | 
						||
| 
								 | 
							
								  memo[attr.keyName] = attr;
							 | 
						||
| 
								 | 
							
								  return memo;
							 | 
						||
| 
								 | 
							
								}, {});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Clipboard extends Module {
							 | 
						||
| 
								 | 
							
								  constructor(quill, options) {
							 | 
						||
| 
								 | 
							
								    super(quill, options);
							 | 
						||
| 
								 | 
							
								    this.quill.root.addEventListener('paste', this.onPaste.bind(this));
							 | 
						||
| 
								 | 
							
								    this.container = this.quill.addContainer('ql-clipboard');
							 | 
						||
| 
								 | 
							
								    this.container.setAttribute('contenteditable', true);
							 | 
						||
| 
								 | 
							
								    this.container.setAttribute('tabindex', -1);
							 | 
						||
| 
								 | 
							
								    this.matchers = [];
							 | 
						||
| 
								 | 
							
								    CLIPBOARD_CONFIG.concat(this.options.matchers).forEach(([selector, matcher]) => {
							 | 
						||
| 
								 | 
							
								      if (!options.matchVisual && matcher === matchSpacing) return;
							 | 
						||
| 
								 | 
							
								      this.addMatcher(selector, matcher);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  addMatcher(selector, matcher) {
							 | 
						||
| 
								 | 
							
								    this.matchers.push([selector, matcher]);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  convert(html) {
							 | 
						||
| 
								 | 
							
								    if (typeof html === 'string') {
							 | 
						||
| 
								 | 
							
								      this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
							 | 
						||
| 
								 | 
							
								      return this.convert();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const formats = this.quill.getFormat(this.quill.selection.savedRange.index);
							 | 
						||
| 
								 | 
							
								    if (formats[CodeBlock.blotName]) {
							 | 
						||
| 
								 | 
							
								      const text = this.container.innerText;
							 | 
						||
| 
								 | 
							
								      this.container.innerHTML = '';
							 | 
						||
| 
								 | 
							
								      return new Delta().insert(text, { [CodeBlock.blotName]: formats[CodeBlock.blotName] });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let [elementMatchers, textMatchers] = this.prepareMatching();
							 | 
						||
| 
								 | 
							
								    let delta = traverse(this.container, elementMatchers, textMatchers);
							 | 
						||
| 
								 | 
							
								    // Remove trailing newline
							 | 
						||
| 
								 | 
							
								    if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
							 | 
						||
| 
								 | 
							
								      delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    debug.log('convert', this.container.innerHTML, delta);
							 | 
						||
| 
								 | 
							
								    this.container.innerHTML = '';
							 | 
						||
| 
								 | 
							
								    return delta;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  dangerouslyPasteHTML(index, html, source = Quill.sources.API) {
							 | 
						||
| 
								 | 
							
								    if (typeof index === 'string') {
							 | 
						||
| 
								 | 
							
								      this.quill.setContents(this.convert(index), html);
							 | 
						||
| 
								 | 
							
								      this.quill.setSelection(0, Quill.sources.SILENT);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      let paste = this.convert(html);
							 | 
						||
| 
								 | 
							
								      this.quill.updateContents(new Delta().retain(index).concat(paste), source);
							 | 
						||
| 
								 | 
							
								      this.quill.setSelection(index + paste.length(), Quill.sources.SILENT);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  onPaste(e) {
							 | 
						||
| 
								 | 
							
								    if (e.defaultPrevented || !this.quill.isEnabled()) return;
							 | 
						||
| 
								 | 
							
								    let range = this.quill.getSelection();
							 | 
						||
| 
								 | 
							
								    let delta = new Delta().retain(range.index);
							 | 
						||
| 
								 | 
							
								    let scrollTop = this.quill.scrollingContainer.scrollTop;
							 | 
						||
| 
								 | 
							
								    this.container.focus();
							 | 
						||
| 
								 | 
							
								    this.quill.selection.update(Quill.sources.SILENT);
							 | 
						||
| 
								 | 
							
								    setTimeout(() => {
							 | 
						||
| 
								 | 
							
								      delta = delta.concat(this.convert()).delete(range.length);
							 | 
						||
| 
								 | 
							
								      this.quill.updateContents(delta, Quill.sources.USER);
							 | 
						||
| 
								 | 
							
								      // range.length contributes to delta.length()
							 | 
						||
| 
								 | 
							
								      this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
							 | 
						||
| 
								 | 
							
								      this.quill.scrollingContainer.scrollTop = scrollTop;
							 | 
						||
| 
								 | 
							
								      this.quill.focus();
							 | 
						||
| 
								 | 
							
								    }, 1);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  prepareMatching() {
							 | 
						||
| 
								 | 
							
								    let elementMatchers = [], textMatchers = [];
							 | 
						||
| 
								 | 
							
								    this.matchers.forEach((pair) => {
							 | 
						||
| 
								 | 
							
								      let [selector, matcher] = pair;
							 | 
						||
| 
								 | 
							
								      switch (selector) {
							 | 
						||
| 
								 | 
							
								        case Node.TEXT_NODE:
							 | 
						||
| 
								 | 
							
								          textMatchers.push(matcher);
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								        case Node.ELEMENT_NODE:
							 | 
						||
| 
								 | 
							
								          elementMatchers.push(matcher);
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								        default:
							 | 
						||
| 
								 | 
							
								          [].forEach.call(this.container.querySelectorAll(selector), (node) => {
							 | 
						||
| 
								 | 
							
								            // TODO use weakmap
							 | 
						||
| 
								 | 
							
								            node[DOM_KEY] = node[DOM_KEY] || [];
							 | 
						||
| 
								 | 
							
								            node[DOM_KEY].push(matcher);
							 | 
						||
| 
								 | 
							
								          });
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    return [elementMatchers, textMatchers];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								Clipboard.DEFAULTS = {
							 | 
						||
| 
								 | 
							
								  matchers: [],
							 | 
						||
| 
								 | 
							
								  matchVisual: true
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function applyFormat(delta, format, value) {
							 | 
						||
| 
								 | 
							
								  if (typeof format === 'object') {
							 | 
						||
| 
								 | 
							
								    return Object.keys(format).reduce(function(delta, key) {
							 | 
						||
| 
								 | 
							
								      return applyFormat(delta, key, format[key]);
							 | 
						||
| 
								 | 
							
								    }, delta);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return delta.reduce(function(delta, op) {
							 | 
						||
| 
								 | 
							
								      if (op.attributes && op.attributes[format]) {
							 | 
						||
| 
								 | 
							
								        return delta.push(op);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        return delta.insert(op.insert, extend({}, {[format]: value}, op.attributes));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }, new Delta());
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function computeStyle(node) {
							 | 
						||
| 
								 | 
							
								  if (node.nodeType !== Node.ELEMENT_NODE) return {};
							 | 
						||
| 
								 | 
							
								  const DOM_KEY = '__ql-computed-style';
							 | 
						||
| 
								 | 
							
								  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function deltaEndsWith(delta, text) {
							 | 
						||
| 
								 | 
							
								  let endText = "";
							 | 
						||
| 
								 | 
							
								  for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
							 | 
						||
| 
								 | 
							
								    let op  = delta.ops[i];
							 | 
						||
| 
								 | 
							
								    if (typeof op.insert !== 'string') break;
							 | 
						||
| 
								 | 
							
								    endText = op.insert + endText;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return endText.slice(-1*text.length) === text;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isLine(node) {
							 | 
						||
| 
								 | 
							
								  if (node.childNodes.length === 0) return false;   // Exclude embed blocks
							 | 
						||
| 
								 | 
							
								  let style = computeStyle(node);
							 | 
						||
| 
								 | 
							
								  return ['block', 'list-item'].indexOf(style.display) > -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function traverse(node, elementMatchers, textMatchers) {  // Post-order
							 | 
						||
| 
								 | 
							
								  if (node.nodeType === node.TEXT_NODE) {
							 | 
						||
| 
								 | 
							
								    return textMatchers.reduce(function(delta, matcher) {
							 | 
						||
| 
								 | 
							
								      return matcher(node, delta);
							 | 
						||
| 
								 | 
							
								    }, new Delta());
							 | 
						||
| 
								 | 
							
								  } else if (node.nodeType === node.ELEMENT_NODE) {
							 | 
						||
| 
								 | 
							
								    return [].reduce.call(node.childNodes || [], (delta, childNode) => {
							 | 
						||
| 
								 | 
							
								      let childrenDelta = traverse(childNode, elementMatchers, textMatchers);
							 | 
						||
| 
								 | 
							
								      if (childNode.nodeType === node.ELEMENT_NODE) {
							 | 
						||
| 
								 | 
							
								        childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) {
							 | 
						||
| 
								 | 
							
								          return matcher(childNode, childrenDelta);
							 | 
						||
| 
								 | 
							
								        }, childrenDelta);
							 | 
						||
| 
								 | 
							
								        childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
							 | 
						||
| 
								 | 
							
								          return matcher(childNode, childrenDelta);
							 | 
						||
| 
								 | 
							
								        }, childrenDelta);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return delta.concat(childrenDelta);
							 | 
						||
| 
								 | 
							
								    }, new Delta());
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return new Delta();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchAlias(format, node, delta) {
							 | 
						||
| 
								 | 
							
								  return applyFormat(delta, format, true);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchAttributor(node, delta) {
							 | 
						||
| 
								 | 
							
								  let attributes = Parchment.Attributor.Attribute.keys(node);
							 | 
						||
| 
								 | 
							
								  let classes = Parchment.Attributor.Class.keys(node);
							 | 
						||
| 
								 | 
							
								  let styles = Parchment.Attributor.Style.keys(node);
							 | 
						||
| 
								 | 
							
								  let formats = {};
							 | 
						||
| 
								 | 
							
								  attributes.concat(classes).concat(styles).forEach((name) => {
							 | 
						||
| 
								 | 
							
								    let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE);
							 | 
						||
| 
								 | 
							
								    if (attr != null) {
							 | 
						||
| 
								 | 
							
								      formats[attr.attrName] = attr.value(node);
							 | 
						||
| 
								 | 
							
								      if (formats[attr.attrName]) return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    attr = ATTRIBUTE_ATTRIBUTORS[name];
							 | 
						||
| 
								 | 
							
								    if (attr != null && (attr.attrName === name || attr.keyName === name)) {
							 | 
						||
| 
								 | 
							
								      formats[attr.attrName] = attr.value(node) || undefined;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    attr = STYLE_ATTRIBUTORS[name]
							 | 
						||
| 
								 | 
							
								    if (attr != null && (attr.attrName === name || attr.keyName === name)) {
							 | 
						||
| 
								 | 
							
								      attr = STYLE_ATTRIBUTORS[name];
							 | 
						||
| 
								 | 
							
								      formats[attr.attrName] = attr.value(node) || undefined;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  if (Object.keys(formats).length > 0) {
							 | 
						||
| 
								 | 
							
								    delta = applyFormat(delta, formats);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchBlot(node, delta) {
							 | 
						||
| 
								 | 
							
								  let match = Parchment.query(node);
							 | 
						||
| 
								 | 
							
								  if (match == null) return delta;
							 | 
						||
| 
								 | 
							
								  if (match.prototype instanceof Parchment.Embed) {
							 | 
						||
| 
								 | 
							
								    let embed = {};
							 | 
						||
| 
								 | 
							
								    let value = match.value(node);
							 | 
						||
| 
								 | 
							
								    if (value != null) {
							 | 
						||
| 
								 | 
							
								      embed[match.blotName] = value;
							 | 
						||
| 
								 | 
							
								      delta = new Delta().insert(embed, match.formats(node));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (typeof match.formats === 'function') {
							 | 
						||
| 
								 | 
							
								    delta = applyFormat(delta, match.blotName, match.formats(node));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchBreak(node, delta) {
							 | 
						||
| 
								 | 
							
								  if (!deltaEndsWith(delta, '\n')) {
							 | 
						||
| 
								 | 
							
								    delta.insert('\n');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchIgnore() {
							 | 
						||
| 
								 | 
							
								  return new Delta();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchIndent(node, delta) {
							 | 
						||
| 
								 | 
							
								  let match = Parchment.query(node);
							 | 
						||
| 
								 | 
							
								  if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) {
							 | 
						||
| 
								 | 
							
								    return delta;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  let indent = -1, parent = node.parentNode;
							 | 
						||
| 
								 | 
							
								  while (!parent.classList.contains('ql-clipboard')) {
							 | 
						||
| 
								 | 
							
								    if ((Parchment.query(parent) || {}).blotName === 'list') {
							 | 
						||
| 
								 | 
							
								      indent += 1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    parent = parent.parentNode;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (indent <= 0) return delta;
							 | 
						||
| 
								 | 
							
								  return delta.compose(new Delta().retain(delta.length() - 1).retain(1, { indent: indent}));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchNewline(node, delta) {
							 | 
						||
| 
								 | 
							
								  if (!deltaEndsWith(delta, '\n')) {
							 | 
						||
| 
								 | 
							
								    if (isLine(node) || (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling))) {
							 | 
						||
| 
								 | 
							
								      delta.insert('\n');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchSpacing(node, delta) {
							 | 
						||
| 
								 | 
							
								  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
							 | 
						||
| 
								 | 
							
								    let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
							 | 
						||
| 
								 | 
							
								    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {
							 | 
						||
| 
								 | 
							
								      delta.insert('\n');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchStyles(node, delta) {
							 | 
						||
| 
								 | 
							
								  let formats = {};
							 | 
						||
| 
								 | 
							
								  let style = node.style || {};
							 | 
						||
| 
								 | 
							
								  if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
							 | 
						||
| 
								 | 
							
								    formats.italic = true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') ||
							 | 
						||
| 
								 | 
							
								                           parseInt(computeStyle(node).fontWeight) >= 700)) {
							 | 
						||
| 
								 | 
							
								    formats.bold = true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (Object.keys(formats).length > 0) {
							 | 
						||
| 
								 | 
							
								    delta = applyFormat(delta, formats);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (parseFloat(style.textIndent || 0) > 0) {  // Could be 0.5in
							 | 
						||
| 
								 | 
							
								    delta = new Delta().insert('\t').concat(delta);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchText(node, delta) {
							 | 
						||
| 
								 | 
							
								  let text = node.data;
							 | 
						||
| 
								 | 
							
								  // Word represents empty line with <o:p> </o:p>
							 | 
						||
| 
								 | 
							
								  if (node.parentNode.tagName === 'O:P') {
							 | 
						||
| 
								 | 
							
								    return delta.insert(text.trim());
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {
							 | 
						||
| 
								 | 
							
								    return delta;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {
							 | 
						||
| 
								 | 
							
								    // eslint-disable-next-line func-style
							 | 
						||
| 
								 | 
							
								    let replacer = function(collapse, match) {
							 | 
						||
| 
								 | 
							
								      match = match.replace(/[^\u00a0]/g, '');    // \u00a0 is nbsp;
							 | 
						||
| 
								 | 
							
								      return match.length < 1 && collapse ? ' ' : match;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
							 | 
						||
| 
								 | 
							
								    text = text.replace(/\s\s+/g, replacer.bind(replacer, true));  // collapse whitespace
							 | 
						||
| 
								 | 
							
								    if ((node.previousSibling == null && isLine(node.parentNode)) ||
							 | 
						||
| 
								 | 
							
								        (node.previousSibling != null && isLine(node.previousSibling))) {
							 | 
						||
| 
								 | 
							
								      text = text.replace(/^\s+/, replacer.bind(replacer, false));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if ((node.nextSibling == null && isLine(node.parentNode)) ||
							 | 
						||
| 
								 | 
							
								        (node.nextSibling != null && isLine(node.nextSibling))) {
							 | 
						||
| 
								 | 
							
								      text = text.replace(/\s+$/, replacer.bind(replacer, false));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return delta.insert(text);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };
							 |