-
Notifications
You must be signed in to change notification settings - Fork 114
Implement class flag that can be used to have an element removed by the parser #414
Conversation
…o be removed by the parser.
I added a class flag In addition, I added a large number of tests for this "editor-only" functionality to ensure it works on container elements, inline elements, self-closing elements, tables, lists, elements with multiple classes, and nested "editor-only" elements. I updated the CHANGELOG with this new feature, but I wasn't sure where I should document the feature in the docs. Let me know if you have a suggestion on where I should include documentation on the feature. |
var TEXT_CONTAINER_ELEMENTS = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', | ||
'pre', 'blockquote', 'div']; | ||
var TEXT_INLINE_ELEMENTS = ['a', 'span', 'strong', 'em']; | ||
var SELF_CLOSING_ELEMENTS = ['br', 'hr', 'img']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There weren't any existing constants in WYMeditor
for these groupings of elements, so I added them here for now. Let me know if you think it would be worthwhile to include them as constants in WYMeditor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We sort of have this defined in parser/xhtml-sax-listener.js
as inline_tags
, but it's not a global. I'll have to think on this a bit. These kind of things are perfect candidates for he StructureManager
that #360 defines
*/ | ||
function htmlEquals(wymeditor, expected) { | ||
function htmlEquals(wymeditor, expected, assertionString) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it useful to be able to include a string message to specify what each assertion in a test was checking, so I added a parameter for including this string message to htmlEquals
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. That's definitely helpful for providing good test output.
@@ -15,6 +15,12 @@ WYMeditor.XhtmlParser = function(Listener, mode) { | |||
this._last_match = ''; | |||
this._current_match = ''; | |||
|
|||
// These are used for removing blocks flagged for removal by the parser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the work that you're doing here actually belongs in the xhtml-sax-listener
. The lexer is responsible for taking in strings of characters and grouping them in to logical HTML tokens, the parser is responsible for classifying these tokens according to the XHTML grammar and the xhtml-sax-listener
is responsible for actually doing interesting things based on events managed by the parser. Deleting things with a certain class, ignoring white space, stripping certain tags, fixing nesting etc. are all things for the sax listener.
All of the state that's not specifically necessary for interpreting grammar should also be in the listener.
Currently, we have some things in the parser that should be there, especially where they're directly managing the listener state (for example the fixNestingBeforeOpeningBlockTag
call), but we want to move away from that as much as possible. The xhtml-parser
should actually be much simpler than it is.
I would do this by:
- Adding a
WYMeditor.XhtmlSaxListener._shouldRemoveTag
method that looks at the class attributes to determine if it has theEDITOR_ONLY_CLASS
- In
WYMeditor.XhtmlSaxListener.prototype.inlineTag
, check if_shouldRemoveTag
and if so, don't add tothis.output
- In
WYMeditor.XhtmlSaxListener.prototype.openBlockTag
, if you_shouldRemoveTag
, then set a flag on the listener.this._insideTagToRemove
to true and use another variable to keep track of where you currently are on the tag stack. Make sure and only do this check if you're not already_insideTagToRemove
. - Then, in all of the other listeners that add to
this.output
(eg.addContent
,openBlockTag
,inlineTag
,closeBlockTag
,closeUnopenedTag
, etc), check the_insideTagToRemove
flag and if it'strue
, don't actually add to the output. - In
WYMeditor.XhtmlSaxListener.prototype.closeBlockTag
, if the flag is set, but we're back down to the same place on the tag stack that triggered the flag, flip the flag back
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, fixNestingBeforeOpeningBlockTag
is not well-implemented and might cause you some problems. You'll probably want to throw in some tests that mix that functionality with the tag removal here.
…listener rather than the parser.
… Chrome and Firefox.
I reimplemented the removal of editor-only elements so that all of the work takes place in I also modified the As for possibly reimplementing |
@@ -240,7 +240,15 @@ WYMeditor.WymClassSafari.prototype.openBlockTag = function (tag, attributes) { | |||
} | |||
} | |||
|
|||
this.output += this.helper.tag(tag, attributes, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't even realize this was a thing. I bet finding this was a PITA. I created #416 as a ticket to stop doing any lexer/parser/listener overrides. All this method does is add confusion.
I love the tests here. Also, good call on not moving the list nesting fix to the parser for now. This solution is actually pretty straight-forward. |
…s for the added Listener flags.
I reformatted the conditionals you pointed out so that they don't use all negatives. Originally, I avoid using I also added comments for all of the variables I added to the |
As far as return, my sensibilities come from the zen of python:
I'm not actually sure if that's what most folks writing javascript prefer, but to me, it's much easier to understand. It means you have to keep fewer things in your head when reading through the code. If I'm indented a level, that's extra context that I need to keep in my brain when evaluating what's happening. If it's flat, then I only need to have verified that the code path I'm following didn't return, which is easy to do when reading top to bottom inside a well-structured method that's not huge/complex. |
// to true. | ||
this._insideTagToRemove = false; | ||
|
||
// If the last tag was not added to the output, this flag is set to true. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this flag? That's more what I would be looking for in this comment. "Why" comments are much better than "what" comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added more description in the comment for this flag.
…ction so it still works in all use cases.
// result in an extra closing LI tag coming up later in the parser. When | ||
// one of these situations occurs, this flag is set to true so that the | ||
// parser knows to ignore the extra closing LI tag. | ||
this._removeExtraLI = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the way this is actually used, it seems like any time this flag is true, the next time you encounter a closing block tag that hasn't already been opened, you ignore it. I'd say this variable should either be called _ignoreNextExtraBlockClose
or some such or the place that uses it should ensure its dealing with an li
tag.
Also, is there any chance you could get in a situation where you would hit two li
tags that you would need to remove? If that's possible, then this flag method would be a problem, since the first one would clear the flag. An alternative way to do that would be a _unopenedBlockClosesToIgnore
list to which you can add tag types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The place that uses this flag does ensure its dealing with an li
tag now, so your first point shouldn't be a problem.
You are correct that it is possible that there would be two or more closing li
tags that need removal (although that would require a pretty horribly broken list to achieve), so I switched this flag to a counter that can be incremented and decremented to keep track of how many extra closing li
tags should be expected.
…nts that wasn't working and added a test for it.
if (this.remove_comments) { | ||
this.output += text; | ||
if (this.remove_comments || this._insideTagToRemove) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? Before if this.remove_comments
was True then the text
would be appended to this.output
. Now if this.remove_comments
is True you return. I am not certain which it should be, just want to make sure it is correct now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed that the original this.remove_comments
was a mistake and should have actually been !this.remove_comments
. It doesn't make much sense that this.remove_comments
being true would cause comments to be included in the output whereas this.remove_comments
being false would cause comments to be removed from the output.
Looks good other than a few code construction issues. You may want to wait for @winhamwr to put his check mark though. |
Looks great! |
Closes #414. Implement class flag that can be used to have an element removed by the parser
In order to allow plugins and other external functions to tell the parser to remove certain elements from the editor's XHTML output, a class flag should be implemented that can be simply added as a class of an element to tell the parser to remove it.