mobiledoc-kit
mobiledoc-kit copied to clipboard
Extensible markup attributes
Originally filed against ember-mobiledoc-editor by mistake, copying to here instead. This is related to #342 but not identical.
I use a custom widget for adding links that accepts both a URL and a checkbox for "Open in New Window". Then I do postEditor.builder.createMarkup('a', { href, target })
. And this is all working great, except I needed to do this:
import { VALID_ATTRIBUTES } from 'mobiledoc-kit/models/markup';
VALID_ATTRIBUTES.push('target');
Mutating a list in another module seems bad. Should we offer a different way to extend this? Whitelisting additional attributes seems like a progressive-enhancement-friendly way of extending mobiledoc. Implementations that don't trust the same set of attributes as you do can ignore them.
@bantic pointed out that allowing target
can be a security problem unless you're taking other precautions. This may be a further argument for extensibility -- this particular feature can be used safely in some contexts but not others, so we wouldn't want it on by default. I admit it's not the greatest example, but there are other legit reasons to want to attach additional metadata beyond just href
to a markup section.
As for reducing interoperability, if there's no way to attach additional metadata to link markup, people are just going to implement custom atoms instead, and that will make interop worse, not better, because the atoms would not progressively degrade as nicely. I think the same argument applies to the discussion about custom tag names in #342: making cards practically portable is much harder than dealing with extended section types that can gracefully degrade to behave exactly the same as p
in implementations that don't understand them.
@ef4 my takeaway from your text is that you advocate for Mobiledoc clinging closer to the web, and to simply be an alternative way to store DOM.
Today, Mobiledoc "duck-types" the behavioral semantics of the web. A p
section behaves like a <p>
tag on the web, an a
markup with a href
attribute of google.com
behaves like <a href="google.com">
. However it doesn't blindly store and render DOM, the constraints are intentionally tighter than that.
I think where there is a missing semantic we think important, in this case "clicking an a
markup opens in a new document instead of replacing the current one" we can feel free to add it to the Mobiledoc format, editors and renderers. Perhaps whitelist target
and a set of valid values, or perhaps we break with the DOM here and store it as targetNewDocument: true
. Lets call that an implementation detail.
Additionally I think like the sectionElementRenderer
we should have a markupElementRender
that allows you to choose how a markup should be rendered. For example the built-in implementation for opening a new doc on a link could render <a rel="noopener" target="_blank">
but you could override it to be <a onclick="window.myOpenFunction(event);">
instead.
At the other extreme, HTML classes in particular are a tough sell. When you render Mobiledoc into HTML for an email or into text for a search engine (use cases in prod today), you know what "open this link in a new document" means and can make a good generic design decision in a shared render. You will never, ever, be able to know what class="weekday"
was supposed to mean in a generic sense.
I think in the specific, having a way to describe the behavior of "Open in New Window" for Mobiledoc links sounds good. Adding other behaviors or styles lifted from the web or publishing world seems good. Adding the ability to push in arbitrary DOM or attributes and classes sounds bad.
I'm a little off on the usage of "progressive" perhaps, but cards and atoms have unknownCardHandler
and unknownAtomHandler
. These could definitely be used for fallback when a specific card or atom isn't present, though I agree that the more you standardize payloads across all cards/atoms the more you can do.
Related: https://github.com/bustlelabs/mobiledoc-dom-renderer/issues/24
Additionally I think like the sectionElementRenderer we should have a markupElementRender that allows you to choose how a markup should be rendered.
Definitely.
At the other extreme, HTML classes in particular are a tough sell.
Completely agree. They can have an undefined impact on the output. Though I would contrast that with something like data attributes, which could legitimately be used for extensions in a controlled and backward-compatible way.
the more you standardize payloads across all cards/atoms the more you can do.
Yes, and that is why I'm saying unknown custom markups and sections are easier to deal with than unknown cards and atoms: their "payload" is dramatically more constrained.
Consider a behavior like <abbr>
. If you implement it as an atom, the payload will be something like { text, title }
, and a renderer that encounters it and doesn't have an implementation can't show anything useful. But if you implement it as a custom markup, the text remains in standard mobiledoc format, and the renderer can ignore its special semantics while still correctly rendering the text itself. The show-title-on-hover behavior becomes progressive enhancement for renderers that understand the markup, without breaking ones that don't.
This may really be an argument for allowing cards and/or atoms to yield
(by analogy to Ember) back to mobiledoc, because that is a similar way to get graceful fallback. The default implementation of a missing card or atom would just be an immediate yield
, so that any mobiledoc inside can still render.
If cards and atoms could yield, then there would be no need to extend markups and sections. I suspect it would even let us unify the concepts, so that a section is just a very simple card, and a markup is just a very simple atom.
I'll mull over your notes before any detailed response @ef4. However I wanted to call out that an atom's text is a first-class arg value
and not part of the payload for exactly the reasons you've described (meaningful fallback). examples in the docs. This would be used in the unknown handler.
No similar structuring of data exists for cards. They are pure "card name" and "payload".