medium-editor
Version:
Medium.com WYSIWYG editor clone.
704 lines (583 loc) • 55.9 kB
JavaScript
/*global selectElementContents,
selectElementContentsAndFire,
fireEvent, prepareEvent,
firePreparedEvent, WORD_PASTE_EXAMPLE */
describe('Pasting content', function () {
'use strict';
var multiLineTests = [
{
source: 'Google docs',
paste: '<meta charset=\'utf-8\'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-b1bb8bfe-f54c-2e1f-72e2-4c7608d2be70"><div dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:bold;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold</span></div><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Italic</span></p><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:bold;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold and Italic</span></p><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">A </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="text-decoration:none;"><span style="font-size:15px;font-family:Arial;color:#1155cc;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;">link</span></a><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">.</span></p></b><br class="Apple-interchange-newline">',
output: '<div><b>Bold</b></div><p><i>Italic</i></p><p><b><i>Bold and Italic</i></b></p><p>A <a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)">link</a>.</p>'
},
{
source: 'Google docs w/ numbered font weight',
paste: '<meta charset=\'utf-8\'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-b1bb8bfe-f54c-2e1f-72e2-4c7608d2be70"><div dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold</span></div><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Italic</span></p><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold and Italic</span></p><br><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"></span><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">A </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="text-decoration:none;"><span style="font-size:15px;font-family:Arial;color:#1155cc;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;">link</span></a><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">.</span></p></b><br class="Apple-interchange-newline">',
output: '<div><b>Bold</b></div><p><i>Italic</i></p><p><b><i>Bold and Italic</i></b></p><p>A <a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)">link</a>.</p>'
},
{
source: 'Inside editor',
paste: '<meta charset=\'utf-8\'><p style="margin-bottom: 40px; color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><b>Bold</b></p><p style="margin-bottom: 40px; color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><i>Italic</i></p><p style="margin-bottom: 40px; color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><b><i>Bold and Italic</i></b></p><p style="margin-bottom: 40px; color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;">A<span class="Apple-converted-space"> </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="color: black;">link</a>.</p>',
output: '<p><b>Bold</b></p><p><i>Italic</i></p><p><b><i>Bold and Italic</i></b></p><p>A <a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)">link</a>.</p>'
},
{
source: 'Messy paragraphs with spaces',
paste: '<meta charset=\'utf-8\'><p class="sub_buzz_desc" style="margin: 0px; padding: 0px 0px 12px; border: 0px; outline: 0px; font-size: 18px; background-color: rgb(255, 255, 255); line-height: 26px; font-style: normal; font-variant: normal; font-weight: normal; font-family: cambria, georgia, serif; color: rgb(34, 34, 34); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-position: initial initial; background-repeat: initial initial;"><b>Bold</b></p><p style="margin: 0px; padding: 6px 0px 12px; border: 0px; outline: 0px; font-size: 18px; background-color: rgb(255, 255, 255); line-height: 26px; font-style: normal; font-variant: normal; font-weight: normal; font-family: cambria, georgia, serif; color: rgb(34, 34, 34); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-position: initial initial; background-repeat: initial initial;"><i>Italic</i></p><p class="sub_buzz_desc" style="margin: 0px; padding: 0px 0px 12px; border: 0px; outline: 0px; font-size: 18px; background-color: rgb(255, 255, 255); line-height: 26px; font-style: normal; font-variant: normal; font-weight: normal; font-family: cambria, georgia, serif; color: rgb(34, 34, 34); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-position: initial initial; background-repeat: initial initial;"><b><i>Bold and Italic</i></b></p><p style="margin: 0px; padding: 6px 0px 12px; border: 0px; outline: 0px; font-size: 18px; background-color: rgb(255, 255, 255); line-height: 26px; font-style: normal; font-variant: normal; font-weight: normal; font-family: cambria, georgia, serif; color: rgb(34, 34, 34); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-position: initial initial; background-repeat: initial initial;">A<span class="Apple-converted-space"> </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 18px; background-color: transparent; color: rgb(0, 119, 238); text-decoration: none; background-position: initial initial; background-repeat: initial initial;">link</a>.</p>',
output: '<p><b>Bold</b></p><p><i>Italic</i></p><p><b><i>Bold and Italic</i></b></p><p>A <a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)">link</a>.</p>'
},
{
source: 'Paragraphs with internal linebreaks',
paste: '<meta charset=\'utf-8\'><p>One<br>Two</p><p>Three<br>Four</p>',
output: '<p>One<br>Two</p><p>Three<br>Four</p>'
},
{
source: 'Non <p> or <div> with only <br> elements',
paste: '<p>One</p><div><h1><br /></h1></div><p>Two</p><span><span><br /></span></span><p>Three</p>',
output: '<p>One</p><p>Two</p><p>Three</p>'
},
{
source: 'Microsoft Word - line breaks',
paste: '<p>One\nTwo\n</p>\n\n<p>Three Four</p>',
output: '<p>One Two </p><p>Three Four</p>'
},
{
source: 'Microsoft Word - Proprietary elements',
paste: '<p>One<o:p></o:p></p><p>Two<o:p></o:p></p>',
output: '<p>One</p><p>Two</p>'
},
{
source: 'Microsoft Word - full document paste',
paste: WORD_PASTE_EXAMPLE,
output: '<p>Mycomplicated <b>word document renders</b> <i>like this in the card</i> generator.</p><p>Testbig text</p><p>Testingsmaller text and <s>crossed out text.</s></p><p>· Test list</p><p>· Test</p><p>o Testindented</p><p>· Tes6t</p><p>1. tes t test test</p><p>a. tes t indented</p>'
}
],
inlineTests = [
{
source: 'Google docs',
paste: '<meta charset=\'utf-8\'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-2f060cc5-1888-a396-af95-bfb31478c7ae"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:bold;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold,</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"> </span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">italic,</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"> </span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:bold;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">bold and italic</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">, and </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="text-decoration:none;"><span style="font-size:15px;font-family:Arial;color:#1155cc;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;">a link</span></a><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">.</span></b>',
output: '<b>Bold,</b> <i>italic,</i> <b><i>bold and italic</i></b>, and <a href="http://en.wikipedia.org/wiki/Link_\\(The_Legend_of_Zelda\\)">a link</a>\\.'
},
{
source: 'Google docs w/ numbered font weight',
paste: '<meta charset=\'utf-8\'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-2f060cc5-1888-a396-af95-bfb31478c7ae"><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Bold,</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"> </span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">italic,</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;"> </span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">bold and italic</span><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">, and </span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="text-decoration:none;"><span style="font-size:15px;font-family:Arial;color:#1155cc;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;">a link</span></a><span style="font-size:15px;font-family:Arial;color:#000000;background-color:transparent;font-weight:normal;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">.</span></b>',
output: '<b>Bold,</b> <i>italic,</i> <b><i>bold and italic</i></b>, and <a href="http://en.wikipedia.org/wiki/Link_\\(The_Legend_of_Zelda\\)">a link</a>\\.'
},
{
source: 'Inside editor',
paste: '<meta charset=\'utf-8\'><b style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Bold,</b><span style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;"> </span><i style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;">italic,</i><span style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;"><span class="Apple-converted-space"> </span></span><b style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><i>bold and italic</i></b><span style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;">, and<span class="Apple-converted-space"> </span></span><a href="http://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)" style="color: black; font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;">a link</a><span style="color: rgb(0, 0, 0); font-family: \'Helvetica Neue\', Helvetica, Arial, sans-serif; font-size: 22.22222328186035px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 30px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;">.</span>',
output: '<b>Bold,</b> <i>italic,</i> <b><i>bold and italic</i></b>, and <a href="http://en.wikipedia.org/wiki/Link_\\(The_Legend_of_Zelda\\)">a link</a>\\.'
},
{
source: 'Copy and pasted anchor from chrome',
paste: '<meta charset=\'utf-8\'><a href="http://www.yahoo.com/" style="box-sizing: border-box; color: rgb(8, 158, 0); text-decoration: none; outline: 0px; font-family: \'Open Sans\', HelveticaNeue, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 26px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(249, 249, 249);">Yahoo</a><span style="color: rgb(62, 67, 62); font-family: \'Open Sans\', HelveticaNeue, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 26px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none; background-color: rgb(249, 249, 249);"><span class="Apple-converted-space"> </span>has been busy rebuilding its business around<span class="Apple-converted-space"> </span></span><a href="http://www.fastcompany.com/3044281/marissa-mayer" style="box-sizing: border-box; color: rgb(8, 158, 0); text-decoration: none; outline: 0px; font-family: \'Open Sans\', HelveticaNeue, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 26px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(249, 249, 249);">mobile</a><span style="color: rgb(62, 67, 62); font-family: \'Open Sans\', HelveticaNeue, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 26px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none; background-color: rgb(249, 249, 249);"> under CEO Marissa Mayer, and soon it could make one of its biggest bets yet on the platform.</span>',
output: '<a href="http://www.yahoo.com/">Yahoo</a> has been busy rebuilding its business around <a href="http://www.fastcompany.com/3044281/marissa-mayer">mobile</a> under CEO Marissa Mayer, and soon it could make one of its biggest bets yet on the platform.'
},
{
source: 'Copy and pasted span with multiple child nodes',
paste: '<span>| </span><span><a href="http://www.yahoo.com/">Yahoo</a> | <a href="http://www.google.com/">Google</a></span>',
output: '\\| <a href="http://www.yahoo.com/">Yahoo</a> \\| <a href="http://www.google.com/">Google</a>'
}
],
textTests = [
{
source: 'Text single word',
paste: 'supercalifragilisticexpalidocious',
output: '<div id="editor-inner">supercalifragilisticexpalidocious</div>'
},
{
source: 'Text single word with leading/trailing space',
paste: ' supercalifragilisticexpalidocious ',
output: '<div id="editor-inner"> supercalifragilisticexpalidocious </div>'
},
{
source: 'Text multi-word with no line breaks',
paste: 'Their relationship consisted in discussing if it existed',
output: '<div id="editor-inner">Their relationship consisted in discussing if it existed</div>'
},
{
source: 'Text with multiple line breaks',
paste: 'Only one thing made him happy\nAnd now that it was gone\nEverything made him happy\r\n',
output: '<div id="editor-inner"><p>Only one thing made him happy</p><p>And now that it was gone</p><p>Everything made him happy</p></div>'
}
];
beforeEach(function () {
setupTestHelpers.call(this);
this.el = this.createElement('div', 'editor', 'hhh');
this.el.id = 'paste-editor';
});
afterEach(function () {
this.cleanupTest();
});
describe('using cleanPastedHTML option', function () {
it('should filter multi-line rich-text pastes', function () {
var i,
editorEl = this.el,
editor = this.newMediumEditor('.editor', {
delay: 200,
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteHandler = editor.getExtensionByName('paste'),
// mock event with clipboardData API
// test requires creating a function, so can't loop or jslint balks
evt = {
pasteText: null,
preventDefault: function () {
return;
},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return this.pasteText;
}
}
};
for (i = 0; i < multiLineTests.length; i += 1) {
// move caret to editor
editorEl.innerHTML = '<span id="editor-inner"> </span>';
selectElementContentsAndFire(editorEl);
evt.clipboardData.pasteText = multiLineTests[i].paste;
pasteHandler.handlePaste(evt, editorEl);
jasmine.clock().tick(100);
expect(editorEl.innerHTML).toEqual(multiLineTests[i].output);
}
});
it('should add paragraphs to pasted plain-text', function () {
var editorEl = this.el,
editor = this.newMediumEditor('.editor', {
delay: 200,
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteHandler = editor.getExtensionByName('paste'),
// mock event with clipboardData API
// test requires creating a function, so can't loop or jslint balks
evt = {
preventDefault: function () {
return;
},
clipboardData: {
types: ['text/plain'],
getData: function (clipboardType) {
if (clipboardType === 'text/plain') {
return 'One\n\nTwo\n\nThree';
}
}
}
};
// move caret to editor
editorEl.innerHTML = '<span id="editor-inner"> </span>';
selectElementContentsAndFire(editorEl);
pasteHandler.handlePaste(evt, editorEl);
jasmine.clock().tick(100);
expect(editorEl.innerHTML).toEqual('<p>One</p><p>Two</p><p>Three</p>');
});
it('should trigger editablePaste', function () {
var editorEl = this.el,
editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editablePaste', spy);
// move caret to editor
editorEl.innerHTML = '<span id="editor-inner"> </span>';
selectElementContentsAndFire(editorEl);
expect(spy).not.toHaveBeenCalled();
var evt = prepareEvent(editorEl, 'paste');
firePreparedEvent(evt, editorEl, 'paste');
jasmine.clock().tick(1);
expect(spy).toHaveBeenCalledWith(evt, this.el);
});
it('should filter multi-line rich-text pastes when "insertHTML" command is not supported', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
spyOn(document, 'queryCommandSupported').and.returnValue(false);
multiLineTests.forEach(function (test) {
this.el.innerHTML = '<span id="editor-inner">lorem ipsum</span>';
selectElementContentsAndFire(this.el);
editor.cleanPaste(test.paste);
expect(this.el.innerHTML).toEqual(test.output);
}.bind(this));
});
it('should add target="_blank" on anchor', function () {
var editor = this.newMediumEditor('.editor', {
targetBlank: true,
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
spyOn(document, 'queryCommandSupported').and.returnValue(false);
this.el.innerHTML = '<span id="editor-inner">lorem ipsum</span>';
selectElementContentsAndFire(this.el);
editor.cleanPaste('<a href="http://0.0.0.0/bar.html">foo<a>');
expect(this.el.innerHTML).toContain('target="_blank"');
});
});
describe('using keyboard', function () {
it('should insert a custom paste-bin on keydown of CTRL + V', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var pasteBin = contentEditables[1];
expect(pasteBin.innerHTML).toBe('%ME_PASTEBIN%');
expect(pasteBin.parentNode).toBe(document.body);
var range = document.getSelection().getRangeAt(0);
expect(MediumEditor.util.isDescendant(pasteBin, range.commonAncestorContainer, true)).toBe(true, 'Select is not within the paste bin');
expect(range.toString()).toBe('%ME_PASTEBIN%');
});
it('should trigger handlePasteBinPaste when pasting into paste-bin', function () {
spyOn(MediumEditor.extensions.paste.prototype, 'handlePasteBinPaste').and.callThrough();
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteHandler = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var pasteBin = contentEditables[1],
evt = prepareEvent(pasteBin, 'paste');
firePreparedEvent(evt, pasteBin, 'paste');
expect(pasteHandler.handlePasteBinPaste).toHaveBeenCalledWith(evt);
});
it('should fire editablePaste event when pasting', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editablePaste', spy);
selectElementContentsAndFire(editor.elements[0].firstChild);
expect(spy).not.toHaveBeenCalled();
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
defaultPrevented: false,
preventDefault: function () {},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'pasted content';
}
}
},
pasteExtension = editor.getExtensionByName('paste');
pasteExtension.handlePasteBinPaste(evt);
jasmine.clock().tick(1);
expect(spy).toHaveBeenCalledWith({ currentTarget: editor.elements[0], target: editor.elements[0] }, editor.elements[0]);
});
it('should do nothing if default was prevented on paste event of the paste-bin', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
defaultPrevented: true,
preventDefault: function () {},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'pasted content';
}
}
};
spyOn(evt, 'preventDefault');
// Paste should insert data from the clipboard, and prevent paste from happening in the paste-bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
expect(this.el.innerHTML).toBe('hhh');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use clipboard data if available and not allow paste to happen', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'pasted content';
}
}
};
spyOn(evt, 'preventDefault');
// Paste should insert data from the clipboard, and prevent paste from happening in the paste-bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).toHaveBeenCalled();
expect(this.el.innerHTML).toBe('pasted content');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use html from the paste bin when clipboard data is not available', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return null;
}
}
},
testHTML = '<b>HTML from <u>paste bin</u></b>',
pasteBin = contentEditables[1];
pasteBin.innerHTML = testHTML;
spyOn(evt, 'preventDefault');
// Paste should not be prevented on the paste bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
jasmine.clock().tick(1);
// HTML from paste-bin should now be in the editor
expect(this.el.innerHTML).toMatch(new RegExp(testHTML + '(<br/?>)?'));
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use plain text when clipboard data is not available and paste-bin does not have real content', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'Plain Text';
}
}
},
pasteBin = contentEditables[1];
pasteBin.innerHTML = '';
spyOn(evt, 'preventDefault');
// Paste should not be prevented on the paste bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
jasmine.clock().tick(1);
// plaintext from the clipboard should now be in the editor
expect(this.el.innerHTML).toBe('Plain Text');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
});
describe('using cleanPaste', function () {
it('should filter inline rich-text', function () {
var i,
editorEl = this.el,
editor = this.newMediumEditor('.editor', {
delay: 200,
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
for (i = 0; i < inlineTests.length; i += 1) {
// move caret to editor
editorEl.innerHTML = 'Before <span id="editor-inner"> </span> after.';
selectElementContents(document.getElementById('editor-inner'));
editor.cleanPaste(inlineTests[i].paste);
jasmine.clock().tick(100);
// Firefox and IE: doing an insertHTML while this <span> is selected results in the html being inserted inside of the span
// Firefox replace the other either side of the <span> with a space
// Webkit: doing an insertHTML while this <span> is selected results in the span being replaced completely
expect(editorEl.innerHTML).toMatch(new RegExp('^Before( |\\s)(<span id="editor-inner">)?' + inlineTests[i].output + '(</span>)?( |\\s)after\\.$'));
}
});
it('should filter inline rich-text when "insertHTML" command is not supported', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
spyOn(document, 'queryCommandSupported').and.returnValue(false);
inlineTests.forEach(function (test) {
this.el.innerHTML = 'Before <span id="editor-inner"> </span> after.';
selectElementContents(document.getElementById('editor-inner'));
editor.cleanPaste(test.paste);
// Firefox and IE: doing an insertHTML while this <span> is selected results in the html being inserted inside of the span
// Firefox replace the other either side of the <span> with a space
// Webkit: doing an insertHTML while this <span> is selected results in the span being replaced completely
expect(this.el.innerHTML).toMatch(new RegExp('^Before( |\\s)(<span id="editor-inner">)?' + test.output + '(</span>)?( |\\s)after\\.$'));
}.bind(this));
});
it('should respect custom replacments when passed during instantiation', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true,
cleanReplacements: [[new RegExp(/<label>/gi), '<sub>'], [new RegExp(/<\/label>/gi), '</sub>']]
}
});
this.el.innerHTML = 'Before <span id="editor-inner"> </span> after.';
selectElementContents(document.getElementById('editor-inner'));
editor.cleanPaste('<label>div one</label><label>div two</label>');
expect(this.el.innerHTML).toMatch(new RegExp('^Before( |\\s)(<span id="editor-inner">)?<sub>div one</sub><sub>div two</sub>(</span>)?( |\\s)after\\.$'));
});
it('should respect custom replacements before builtin replacements.', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true,
preCleanReplacements: [[new RegExp(/<\/?o:[a-z]*>/gi), 'foo']]
}
});
this.el.innerHTML = 'Before <span id="editor-inner"> </span> after.';
selectElementContents(document.getElementById('editor-inner'));
// Normally, the paste extension's regular expressions would clear the `<o:p></o:p>` tags,
// but our `preCleanReplacements` should transform them each to "foo" before the default
// cleanReplacement has a chance to see it.
editor.cleanPaste('<div><o:p></o:p></div>');
expect(this.el.innerHTML).toMatch(new RegExp('foofoo'));
});
it('should cleanup only pasted element on multi-line when nothing is selected', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
this.el.innerHTML = '<div><img src="http://0.0.0.0/ohyeah.png" /></div>';
selectElementContents(this.el.firstChild, { collapse: true });
editor.cleanPaste('<table><tr><td>test</td><td><br/></td></tr></table>');
expect(this.el.innerHTML).toContain('<img src="http://0.0.0.0/ohyeah.png"></div>');
});
});
describe('using pasteHTML', function () {
it('should remove certain attributes and tags by default', function () {
var editor = this.newMediumEditor('.editor');
selectElementContents(this.el.firstChild);
editor.pasteHTML('<p class="some-class" style="font-weight: bold" dir="ltr"><meta name="description" content="test" />test</p>');
expect(editor.elements[0].innerHTML).toBe('<p>test</p>');
});
it('should not remove node with "empty" content', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
this.el.innerHTML = '<div>this is a div</div><figure id="editor-inner">and this is a figure</figure>.';
selectElementContents(this.el.firstChild);
editor.pasteHTML('<table><tr><td>test</td><td><br/></td></tr></table>');
expect(this.el.innerHTML).toContain('<table><tbody><tr><td>test</td><td><br></td></tr></tbody></table>');
});
it('should accept a list of attrs to clean up', function () {
var editor = this.newMediumEditor('.editor');
selectElementContents(this.el.firstChild);
editor.pasteHTML(
'<table class="medium-editor-table" dir="ltr" style="border: 1px solid red;"><tbody><tr><td>test</td></tr></tbody></table>',
{ cleanAttrs: ['style',