ParseDOM Span class copy/paste issue

I am trying to separate my concerns such that any mark should be a span which is either styled (in the case of color) or classed (in the case of stuff like bold/underline/italic/etc.) The whole thing works quite happily until I try to copy/paste stuff. the toDOM event is only called once on my default “span” (which is the mark used to control color) mark. This means that If i have copied a selection with strong and italic marks, only the italic will be applied on the paste because it has precedence over strong. I wouldn’t mind that if I were able use the DOMOutputSpec to supply the children (which I can get quite easily from the parseDOM event).

the instructions for DOMOutputSpecs in the ProseMirror documentation say that they can be:

An array [that] describes a DOM element. The first value in the array should be a string—the name of the DOM element. If the second element is plain object, it is interpreted as a set of attributes for the element. Any elements after that (including the 2nd if it’s not an attribute object) are interpreted as children of the DOM elements, and must either be valid DOMOutputSpec values, or the number zero.

thus I should be able to write the below:

return [
      'span',
      Object.assign(
        {},
        mark.attrs && mark.attrs.style ? { style: mark.attrs.style } : {},
        mark.attrs && mark.attrs.class ? { class: mark.attrs.class } : {}),
      [
        'span',
        Object.assign(
          {},
          {class: 'some other class etc'}),
        [ 'span', { ... } ]
      ]
    ]

[ EDIT This works in theory as long as you make sure that the innermost child has the “hole” at the very end of it.

However!!! as far as ProseMirror is concerned, it still thinks you’ve only inserted a single mark. Thus the commands are not behaving correctly on the pasted items… honestly, I’m really stuck. I feel like I’m not doing things how prose mirror wants me to, but i really want to separate my concerns and I can’t see how else to go without having more than one mark use a span tag. Suggestions welcome ]

In case any of the above has lost you, here’s a video demonstrating the problem. I make some text bold and italic, then copy/paste it. My commands are rendered useless in the process.

uhoh

By having each span’s parseDOM return false if its criteria aren’t met (ie. must have an explicit classname/style), I seem to be able to filter. This kind of works, but now I’m getting a recursion error around this line in ProseMirror’s index.js:

function compareDeep(a, b) {
  if (a === b) { return true }
  if (!(a && typeof a == "object") ||
      !(b && typeof b == "object")) { return false }
  var array = Array.isArray(a);
  if (Array.isArray(b) != array) { return false }
  if (array) {
    if (a.length != b.length) { return false }
    for (var i = 0; i < a.length; i++) { if (!compareDeep(a[i], b[i])) { return false } }
  } else {
    for (var p in a) { if (!(p in b) || !compareDeep(a[p], b[p])) { return false } }
    for (var p$1 in b) { if (!(p$1 in a)) { return false } }
  }
  return true
}

every time I think I’m getting a little closer, something else whacks me :confused:

here’s 2 examples from my schema:

strikes text through the middle:

export default {
  attrs: {
    class: {
      default: 'pm-strikethrough'
    }
  },
  group: 'inline',
  inclusive: false,
  parseDOM: [{
    tag: 'span',
    getAttrs: mark => {
      return (
        mark &&
        mark.attributes &&
        mark.attributes.class &&
        mark.attributes.class.value === 'pm-strikethrough')
        ? { class: 'pm-strikethrough' }
        : false
    }
  }],
  toDOM: (mark) => ['span', { class: 'pm-strikethrough' }, 0]
}

colors text:

export default {
  attrs: {
    style: { default: 'color: rgba(0,0,0,255)' }
  },
  group: 'inline',
  parseDOM: [{
    tag: 'span',
    getAttrs: mark => {
      return (
        mark &&
        mark.attributes &&
        !mark.attributes.class &&
        mark.attributes.style &&
        /^color/.test(mark.attributes.style.value))
        ? { style: mark.attributes.style }
        : false
    }
  }],
  toDOM: (mark) => {
      return [
        'span',
        { style: typeof mark.attrs.style === 'object'
          ? mark.attrs.style.value
          : mark.attrs.style },
        0
      ]
  }
}

after a LOT of testing and analysis, I’m fairly sure that something weird is going on with webpack and prosemirror. Some kind of conflict leads prosemirror to continue to compare style attributes indefinitely. It seems to be specifically related to applying colors as styles. I’ve raised an issue on prosemirror-model as - given this is an NPM module for ECMA^6 JS - the majority of clients are likely to develop with webpack (plus I’m not wholly certain that webpack is the cause of the problem).

resolved, the answer is simple though the cause is opaque:

toDOM cannot parse mark.attributes.style.value properly. Make sure therefore that - where necessary - you perform that activity in parseDOM instead. Hoooo boy, that has taken hours to sort out. I’ll close the Github issue