fix(composer): much better specs for composer & quoted text

Summary: Fixed a bug bug with the quoted text clearing the bodies on replies

Test Plan: all the tests

Reviewers: dillon, bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D1981
This commit is contained in:
Evan Morikawa 2015-09-03 19:41:56 -07:00
parent 1daeaff367
commit ee41722891
9 changed files with 246 additions and 142 deletions

View file

@ -106,9 +106,17 @@ class ContenteditableComponent extends React.Component
onInput={@_onInput}
onKeyDown={@_onKeyDown}
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
<a className={@_quotedTextClasses()} onClick={@_onToggleQuotedText}></a>
{@_renderQuotedTextControl()}
</div>
_renderQuotedTextControl: ->
if QuotedHTMLParser.hasQuotedHTML(@props.html)
text = if @props.mode?.showQuotedText then "Hide" else "Show"
<a className="quoted-text-control" onClick={@_onToggleQuotedText}>
<span className="dots">&bull;&bull;&bull;</span>{text} previous
</a>
else return null
focus: =>
@_editableNode().focus()
@ -1096,7 +1104,5 @@ class ContenteditableComponent extends React.Component
_quotedTextClasses: => classNames
"quoted-text-control": true
"no-quoted-text": not QuotedHTMLParser.hasQuotedHTML(@props.html)
"show-quoted-text": @props.mode?.showQuotedText
module.exports = ContenteditableComponent

View file

@ -107,9 +107,11 @@ useDraft = (draftAttributes={}) ->
@draft = new Message _.extend({draft: true, body: ""}, draftAttributes)
draft = @draft
proxy = draftStoreProxyStub(DRAFT_CLIENT_ID, @draft)
@proxy = proxy
spyOn(ComposerView.prototype, "componentWillMount").andCallFake ->
# NOTE: This is called in the context of the component.
@_prepareForDraft(DRAFT_CLIENT_ID)
@_setupSession(proxy)
@ -118,8 +120,7 @@ useDraft = (draftAttributes={}) ->
# `componentWillMount`, we manually call sessionForClientId to make this
# part of the test synchronous. We need to make the `then` block of the
# sessionForClientId do nothing so `_setupSession` is not called twice!
spyOn(DraftStore, "sessionForClientId").andCallFake ->
then: ->
spyOn(DraftStore, "sessionForClientId").andCallFake -> then: ->
useFullDraft = ->
useDraft.call @,
@ -141,6 +142,76 @@ describe "populated composer", ->
@isSending = {state: false}
spyOn(DraftStore, "isSendingDraft").andCallFake => @isSending.state
describe "when sending a new message", ->
it 'makes a request with the message contents', ->
useDraft.call @
makeComposer.call @
editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
spyOn(@proxy.changes, "add")
editableNode.innerHTML = "Hello <strong>world</strong>"
ReactTestUtils.Simulate.input(editableNode)
expect(@proxy.changes.add).toHaveBeenCalled()
expect(@proxy.changes.add.calls.length).toBe 1
body = @proxy.changes.add.calls[0].args[0].body
expect(body).toBe "<head></head><body>Hello <strong>world</strong></body>"
describe "when sending a reply-to message", ->
beforeEach ->
@replyBody = """<blockquote class="gmail_quote">On Sep 3 2015, at 12:14 pm, Evan Morikawa &lt;evan@evanmorikawa.com&gt; wrote:<br>This is a test!</blockquote>"""
useDraft.call @,
from: [u1]
to: [u2]
subject: "Test Reply Message 1"
body: @replyBody
makeComposer.call @
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
spyOn(@proxy.changes, "add")
it 'begins with the replying message collapsed', ->
expect(@editableNode.innerHTML).toBe ""
it 'saves the full new body, plus quoted text', ->
@editableNode.innerHTML = "Hello <strong>world</strong>"
ReactTestUtils.Simulate.input(@editableNode)
expect(@proxy.changes.add).toHaveBeenCalled()
expect(@proxy.changes.add.calls.length).toBe 1
body = @proxy.changes.add.calls[0].args[0].body
expect(body).toBe """<head></head><body>Hello <strong>world</strong>#{@replyBody}</body>"""
describe "when sending a forwarded message message", ->
beforeEach ->
@fwdBody = """<br><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
Begin forwarded message:
<br><br>
From: Evan Morikawa &lt;evan@evanmorikawa.com&gt;<br>Subject: Test Forward Message 1<br>Date: Sep 3 2015, at 12:14 pm<br>To: Evan Morikawa &lt;evan@nylas.com&gt;
<br><br>
<meta content="text/html; charset=us-ascii">This is a test!
</blockquote>"""
useDraft.call @,
from: [u1]
to: [u2]
subject: "Fwd: Test Forward Message 1"
body: @fwdBody
makeComposer.call @
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
spyOn(@proxy.changes, "add")
it 'begins with the forwarded message expanded', ->
expect(@editableNode.innerHTML).toBe @fwdBody
it 'saves the full new body, plus forwarded text', ->
@editableNode.innerHTML = "Hello <strong>world</strong>#{@fwdBody}"
ReactTestUtils.Simulate.input(@editableNode)
expect(@proxy.changes.add).toHaveBeenCalled()
expect(@proxy.changes.add.calls.length).toBe 1
body = @proxy.changes.add.calls[0].args[0].body
expect(body).toBe """Hello <strong>world</strong>#{@fwdBody}"""
describe "When displaying info from a draft", ->
beforeEach ->
useFullDraft.apply(@)

View file

@ -11,106 +11,112 @@ ContenteditableComponent = require "../lib/contenteditable-component",
describe "ContenteditableComponent", ->
beforeEach ->
@onChange = jasmine.createSpy('onChange')
@html = 'Test <strong>HTML</strong><br>'
@component = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@html} onChange={@onChange}/>
)
@htmlNoQuote = 'Test <strong>HTML</strong><br>'
@htmlWithQuote = 'Test <strong>HTML</strong><br><blockquote class="gmail_quote">QUOTE</blockquote>'
@htmlWithQuote = 'Test <strong>HTML</strong><br><br><blockquote class="gmail_quote">QUOTE</blockquote>'
@componentWithQuote = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlWithQuote}
onChange={@onChange}
mode={showQuotedText: false}/>
)
# Must be called with the test's scope
setHTML = (newHTML) ->
@$contentEditable.innerHTML = newHTML
ReactTestUtils.Simulate.input(@$contentEditable, {target: {value: newHTML}})
describe "quoted-text-control", ->
it "should be rendered", ->
expect(ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')).toBeDefined()
describe "quoted-text-control toggle button", ->
it "should be visible if the html contains quoted text", ->
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
expect(@toggle.props.className.indexOf('no-quoted-text') >= 0).toBe(false)
it "should be have `show-quoted-text` if showQuotedText is true", ->
@componentWithQuote = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlWithQuote} onChange={@onChange} mode={showQuotedText: true}/>
)
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
expect(@toggle.props.className.indexOf('show-quoted-text') >= 0).toBe(true)
it "should not have `show-quoted-text` if showQuotedText is false", ->
@componentWithQuote.setState(showQuotedText: false)
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
expect(@toggle.props.className.indexOf('show-quoted-text') >= 0).toBe(false)
it "should be hidden otherwise", ->
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
expect(@toggle.props.className.indexOf('no-quoted-text') >= 0).toBe(true)
describe "when showQuotedText is false", ->
it "should not display quoted text", ->
@editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(@componentWithQuote, 'contentEditable')
expect(React.findDOMNode(@editDiv).innerHTML).toEqual @html
describe "when showQuotedText is true", ->
describe "when there's no quoted text", ->
beforeEach ->
@componentWithQuote = ReactTestUtils.renderIntoDocument(
@contentEditable = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlNoQuote}
onChange={@onChange}
mode={showQuotedText: true}/>)
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
it 'should not display any quoted text', ->
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
it "allows the text to update", ->
textToAdd = "MORE <strong>TEXT</strong>!"
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
setHTML.call(@, textToAdd + @htmlNoQuote)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(textToAdd + @htmlNoQuote)
it 'should not render the quoted-text-control toggle', ->
toggles = ReactTestUtils.scryRenderedDOMComponentsWithClass(@contentEditable, 'quoted-text-control')
expect(toggles.length).toBe 0
describe 'when showQuotedText is true', ->
beforeEach ->
@contentEditable = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlWithQuote}
onChange={@onChange}
mode={showQuotedText: true}/>
)
mode={showQuotedText: true}/>)
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
it "should display all the HTML", ->
@componentWithQuote.setState(showQuotedText: true)
@editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(@componentWithQuote, 'contentEditable')
expect(React.findDOMNode(@editDiv).innerHTML.indexOf('gmail_quote') >= 0).toBe(true)
it 'should display the quoted text', ->
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
describe "showQuotedText", ->
it "should default to false", ->
expect(@component.props.mode?.showQuotedText).toBeUndefined()
it "should call `props.onChange` with the entire HTML string", ->
textToAdd = "MORE <strong>TEXT</strong>!"
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
setHTML.call(@, textToAdd + @htmlWithQuote)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(textToAdd + @htmlWithQuote)
describe "when the html is changed", ->
beforeEach ->
@changedHtmlWithoutQuote = '<head></head><body>Changed <strong>NEW 1 HTML</strong><br><br></body>'
@changedHtmlWithQuote = 'Changed <strong>NEW 1 HTML</strong><br><br><blockquote class="gmail_quote">QUOTE</blockquote>'
it "should allow the quoted text to be changed", ->
newText = 'Test <strong>NEW 1 HTML</strong><blockquote class="gmail_quote">QUOTE CHANGED!!!</blockquote>'
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
setHTML.call(@, newText)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(newText)
@performEdit = (newHTML, component = @componentWithQuote) =>
editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(component, 'contentEditable')
React.findDOMNode(editDiv).innerHTML = newHTML
ReactTestUtils.Simulate.input(editDiv, {target: {value: newHTML}})
describe "when showQuotedText is true", ->
describe 'quoted text control toggle button', ->
beforeEach ->
@componentWithQuote = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlWithQuote}
onChange={@onChange}
mode={showQuotedText: true}/>
)
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@contentEditable, 'quoted-text-control')
it "should call `props.onChange` with the entire HTML string", ->
@componentWithQuote.setState(showQuotedText: true)
@performEdit(@changedHtmlWithQuote)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(@changedHtmlWithQuote)
it 'should be rendered', ->
expect(@toggle).toBeDefined()
it "should allow the quoted text to be changed", ->
changed = 'Test <strong>NEW 1 HTML</strong><blockquote class="gmail_quote">QUOTE CHANGED!!!</blockquote>'
@componentWithQuote.setState(showQuotedText: true)
@performEdit(changed)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(changed)
it 'prompts to hide the quote', ->
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Hide previous"
describe "when showQuotedText is false", ->
it "should let you change the text, and then append the quoted text part to the end before firing `onChange`", ->
@componentWithQuote.setState(showQuotedText: false)
@performEdit(@changedHtmlWithoutQuote)
ev = @onChange.mostRecentCall.args[0]
withQuote = "<head></head><body>#{@changedHtmlWithQuote}</body>"
expect(ev.target.value).toEqual(withQuote)
describe 'when showQuotedText is false', ->
beforeEach ->
@contentEditable = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={@htmlWithQuote}
onChange={@onChange}
mode={showQuotedText: false}/>)
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
it "should work if the component does not contain quoted text", ->
changed = '<head></head><body>Hallooo! <strong>NEW 1 HTML HTML HTML</strong><br></body>'
@component.setState(showQuotedText: true)
@performEdit(changed, @component)
ev = @onChange.mostRecentCall.args[0]
expect(ev.target.value).toEqual(changed)
# The quoted text dom parser wraps stuff inertly in body tags
wrapBody = (html) -> "<head></head><body>#{html}</body>"
it 'should not display any quoted text', ->
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
it "should let you change the text, and then append the quoted text part to the end before firing `onChange`", ->
textToAdd = "MORE <strong>TEXT</strong>!"
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
setHTML.call(@, textToAdd + @htmlNoQuote)
ev = @onChange.mostRecentCall.args[0]
# Note that we expect the version WITH a quote while setting the
# version withOUT a quote.
expect(ev.target.value).toEqual(wrapBody(textToAdd + @htmlWithQuote))
it "should let you add more html that looks like quoted text, and still properly appends the old quoted text", ->
textToAdd = "Yo <blockquote class=\"gmail_quote\">I'm a fake quote</blockquote>"
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
setHTML.call(@, textToAdd + @htmlNoQuote)
ev = @onChange.mostRecentCall.args[0]
# Note that we expect the version WITH a quote while setting the
# version withOUT a quote.
expect(ev.target.value).toEqual(wrapBody(textToAdd + @htmlWithQuote))
describe 'quoted text control toggle button', ->
beforeEach ->
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@contentEditable, 'quoted-text-control')
it 'should be rendered', ->
expect(@toggle).toBeDefined()
it 'prompts to hide the quote', ->
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Show previous"

View file

@ -35,7 +35,6 @@ class EmailFrame extends React.Component
!_.isEqual(newProps, @props)
_writeContent: =>
wrapperClass = if @props.showQuotedText then "show-quoted-text" else ""
doc = React.findDOMNode(@).contentDocument
doc.open()
@ -50,7 +49,7 @@ class EmailFrame extends React.Component
EmailFixingStyles = EmailFixingStyles.replace(/.ignore-in-parent-frame/g, '')
if (EmailFixingStyles)
doc.write("<style>#{EmailFixingStyles}</style>")
doc.write("<div id='inbox-html-wrapper' class='#{wrapperClass}'>#{@_emailContent()}</div>")
doc.write("<div id='inbox-html-wrapper'>#{@_emailContent()}</div>")
doc.close()
# Notify the EventedIFrame that we've replaced it's document (with `open`)
@ -80,7 +79,7 @@ class EmailFrame extends React.Component
if @props.showQuotedText
@props.content
else
QuotedHTMLParser.hideQuotedHTML(@props.content)
QuotedHTMLParser.removeQuotedHTML(@props.content, keepIfWholeBodyIsQuote: true)
module.exports = EmailFrame

View file

@ -80,13 +80,21 @@ class MessageItem extends React.Component
<div className="message-item-area">
{@_renderHeader()}
<EmailFrame showQuotedText={@state.showQuotedText} content={@_formatBody()}/>
<a className={@_quotedTextClasses()} onClick={@_toggleQuotedText}></a>
{@_renderQuotedTextControl()}
{@_renderEvents()}
{@_renderAttachments()}
</div>
</div>
</div>
_renderQuotedTextControl: ->
if QuotedHTMLParser.hasQuotedHTML(@props.message.body)
text = if @state.showQuotedText then "Hide" else "Show"
<a className="quoted-text-control" onClick={@_toggleQuotedText}>
<span className="dots">&bull;&bull;&bull;</span>{text} previous
</a>
else return null
_renderHeader: =>
classes = classNames
"message-header": true
@ -168,11 +176,6 @@ class MessageItem extends React.Component
else
<div></div>
_quotedTextClasses: => classNames
"quoted-text-control": true
'no-quoted-text': not QuotedHTMLParser.hasQuotedHTML(@props.message.body)
'show-quoted-text': @state.showQuotedText
_renderHeaderSideItems: ->
styles =
position: "absolute"

View file

@ -246,21 +246,33 @@ describe "MessageItem", ->
describe "showQuotedText", ->
it "should be initialized to false", ->
@createComponent()
expect(@component.state.showQuotedText).toBe(false)
it "should show the `show quoted text` toggle in the off state", ->
it "shouldn't render the quoted text control if there's no quoted text", ->
@message.body = "no quotes here!"
@createComponent()
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
expect(React.findDOMNode(toggle).className.indexOf('show-quoted-text')).toBe(-1)
toggles = ReactTestUtils.scryRenderedDOMComponentsWithClass(@component, 'quoted-text-control')
expect(toggles.length).toBe 0
it "should have the `no quoted text` class if there is no quoted text in the message", ->
spyOn(QuotedHTMLParser, 'hasQuotedHTML').andReturn false
describe 'quoted text control toggle button', ->
beforeEach ->
@message.body = """
Message
<blockquote class="gmail_quote">
Quoted message
</blockquote>
"""
@createComponent()
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
@createComponent()
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
expect(React.findDOMNode(toggle).className.indexOf('no-quoted-text')).not.toBe(-1)
it 'should be rendered', ->
expect(@toggle).toBeDefined()
it 'prompts to hide the quote', ->
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Show previous"
it "should be initialized to true if the message contains `Forwarded`...", ->
@message.body = """
@ -294,14 +306,26 @@ describe "MessageItem", ->
@createComponent()
expect(@component.state.showQuotedText).toBe(false)
describe "when true", ->
describe "when showQuotedText is true", ->
beforeEach ->
@message.body = """
Message
<blockquote class="gmail_quote">
Quoted message
</blockquote>
"""
@createComponent()
@component.setState(showQuotedText: true)
it "should show the `show quoted text` toggle in the on state", ->
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
expect(React.findDOMNode(toggle).className.indexOf('show-quoted-text') > 0).toBe(true)
describe 'quoted text control toggle button', ->
beforeEach ->
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
it 'should be rendered', ->
expect(@toggle).toBeDefined()
it 'prompts to hide the quote', ->
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Hide previous"
it "should pass the value into the EmailFrame", ->
frame = ReactTestUtils.findRenderedComponentWithType(@component, EmailFrameStub)

View file

@ -12,8 +12,8 @@ describe "QuotedHTMLParser", ->
hideQuotedHTML = (fname) ->
return QuotedHTMLParser.hideQuotedHTML(readFile(fname))
removeQuotedHTML = (fname) ->
return QuotedHTMLParser.removeQuotedHTML(readFile(fname))
removeQuotedHTML = (fname, opts={}) ->
return QuotedHTMLParser.removeQuotedHTML(readFile(fname), opts)
numQuotes = (html) ->
re = new RegExp(QuotedHTMLParser.annotationClass, 'g')
@ -21,7 +21,8 @@ describe "QuotedHTMLParser", ->
[1..16].forEach (n) ->
it "properly parses email_#{n}", ->
expect(removeQuotedHTML("email_#{n}.html")).toEqual readFile("email_#{n}_stripped.html")
opts = keepIfWholeBodyIsQuote: true
expect(removeQuotedHTML("email_#{n}.html", opts)).toEqual readFile("email_#{n}_stripped.html")
describe 'manual quote detection tests', ->
@ -258,7 +259,8 @@ describe "QuotedHTMLParser", ->
it 'works with these manual test cases', ->
for {before, after} in tests
test = clean(QuotedHTMLParser.removeQuotedHTML(before))
opts = keepIfWholeBodyIsQuote: true
test = clean(QuotedHTMLParser.removeQuotedHTML(before, opts))
expect(test).toEqual clean(after)
it 'removes all trailing <br> tags except one', ->

View file

@ -8,20 +8,19 @@ class QuotedHTMLParser
# Given an html string, it will add the `annotationClass` to the DOM
# element
hideQuotedHTML: (html) ->
hideQuotedHTML: (html, {keepIfWholeBodyIsQuote}={}) ->
doc = @_parseHTML(html)
quoteElements = @_findQuoteLikeElements(doc)
if not @_wholeBodyIsQuote(doc, quoteElements)
if keepIfWholeBodyIsQuote and @_wholeBodyIsQuote(doc, quoteElements)
return doc.children[0].innerHTML
else
@_annotateElements(quoteElements)
return doc.children[0].innerHTML
return doc.children[0].innerHTML
hasQuotedHTML: (html) ->
doc = @_parseHTML(html)
quoteElements = @_findQuoteLikeElements(doc)
if @_wholeBodyIsQuote(doc, quoteElements)
return false
else
return quoteElements.length > 0
return quoteElements.length > 0
# Public: Removes quoted text from an HTML string
#
@ -34,12 +33,17 @@ class QuotedHTMLParser
# - `options`
# - `includeInline` Defaults false. If true, inline quotes are removed
# too
# - `keepIfWholeBodyIsQuote` Defaults false. If true, then it will
# check to see if the whole html body is a giant quote. If so, it will
# preserve it.
#
# Returns HTML without quoted text
removeQuotedHTML: (html, options={}) ->
doc = @_parseHTML(html)
quoteElements = @_findQuoteLikeElements(doc, options)
if not @_wholeBodyIsQuote(doc, quoteElements)
if options.keepIfWholeBodyIsQuote and @_wholeBodyIsQuote(doc, quoteElements)
return doc.children[0].innerHTML
else
DOMUtils.removeElements(quoteElements, options)
childNodes = doc.body.childNodes
@ -53,14 +57,13 @@ class QuotedHTMLParser
break
DOMUtils.removeElements(extraTailBrTags)
return doc.children[0].innerHTML
return doc.children[0].innerHTML
appendQuotedHTML: (htmlWithoutQuotes, originalHTML) ->
doc = @_parseHTML(originalHTML)
quoteElements = @_findQuoteLikeElements(doc)
if not @_wholeBodyIsQuote(doc, quoteElements)
doc = @_parseHTML(htmlWithoutQuotes)
doc.body.appendChild(node) for node in quoteElements
doc = @_parseHTML(htmlWithoutQuotes)
doc.body.appendChild(node) for node in quoteElements
return doc.children[0].innerHTML
restoreAnnotatedHTML: (html) ->

View file

@ -10,29 +10,19 @@
font-weight: 600;
font-size: @font-size-smaller;
&.no-quoted-text {
display:none;
}
&:hover {
cursor: pointer;
color: @text-color-subtle;
border: 1px solid fade(@text-color-subtle, 35%);
}
&:before {
content:'\2022\2022\2022';
.dots {
display: inline-block;
font-size: @font-size-smaller * 0.8;
top:-1px;
position:relative;
padding-right:8px;
}
&.show-quoted-text:after {
content:'Hide previous';
}
&:after {
content:'Show previous';
}
}
.mail-label {