123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770 |
- // Copyright 2008 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- goog.provide('goog.editor.plugins.EnterHandlerTest');
- goog.setTestOnly('goog.editor.plugins.EnterHandlerTest');
- goog.require('goog.dom');
- goog.require('goog.dom.NodeType');
- goog.require('goog.dom.Range');
- goog.require('goog.dom.TagName');
- goog.require('goog.editor.BrowserFeature');
- goog.require('goog.editor.Field');
- goog.require('goog.editor.Plugin');
- goog.require('goog.editor.plugins.Blockquote');
- goog.require('goog.editor.plugins.EnterHandler');
- goog.require('goog.editor.range');
- goog.require('goog.events');
- goog.require('goog.events.KeyCodes');
- goog.require('goog.testing.ExpectedFailures');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.dom');
- goog.require('goog.testing.editor.TestHelper');
- goog.require('goog.testing.events');
- goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
- var savedHtml;
- var field1;
- var field2;
- var firedDelayedChange;
- var firedBeforeChange;
- var clock;
- var container;
- var EXPECTEDFAILURES;
- function setUpPage() {
- container = goog.dom.getElement('container');
- }
- function setUp() {
- EXPECTEDFAILURES = new goog.testing.ExpectedFailures();
- savedHtml = goog.dom.getElement('root').innerHTML;
- clock = new goog.testing.MockClock(true);
- }
- function setUpFields(classnameRequiredToSplitBlockquote) {
- field1 = makeField('field1', classnameRequiredToSplitBlockquote);
- field2 = makeField('field2', classnameRequiredToSplitBlockquote);
- field1.makeEditable();
- field2.makeEditable();
- }
- function tearDown() {
- clock.dispose();
- EXPECTEDFAILURES.handleTearDown();
- goog.dom.getElement('root').innerHTML = savedHtml;
- }
- function testEnterInNonSetupBlockquote() {
- setUpFields(true);
- resetChangeFlags();
- var prevented = !selectNodeAndHitEnter(field1, 'field1cursor');
- waitForChangeEvents();
- assertChangeFlags();
- // make sure there's just one blockquote, and that the text has been deleted.
- var elem = field1.getElement();
- var dom = field1.getEditableDomHelper();
- EXPECTEDFAILURES.expectFailureFor(
- goog.userAgent.OPERA,
- 'The blockquote is overwritten with DIV due to CORE-22104 -- Opera ' +
- 'overwrites the BLOCKQUOTE ancestor with DIV when doing FormatBlock ' +
- 'for DIV');
- try {
- assertEquals(
- 'Blockquote should not be split', 1,
- dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)
- .length);
- } catch (e) {
- EXPECTEDFAILURES.handleException(e);
- }
- assert(
- 'Selection should be deleted', -1 == elem.innerHTML.indexOf('selection'));
- assertEquals(
- 'The event should have been prevented only on webkit', prevented,
- goog.userAgent.WEBKIT);
- }
- function testEnterInSetupBlockquote() {
- setUpFields(true);
- resetChangeFlags();
- var prevented = !selectNodeAndHitEnter(field2, 'field2cursor');
- waitForChangeEvents();
- assertChangeFlags();
- // make sure there are two blockquotes, and a DIV with nbsp in the middle.
- var elem = field2.getElement();
- var dom = field2.getEditableDomHelper();
- assertEquals(
- 'Blockquote should be split', 2,
- dom.getElementsByTagNameAndClass(goog.dom.TagName.BLOCKQUOTE, null, elem)
- .length);
- assert(
- 'Selection should be deleted', -1 == elem.innerHTML.indexOf('selection'));
- assert(
- 'should have div with ',
- -1 != elem.innerHTML.indexOf('>' + getNbsp() + '<'));
- assert('event should have been prevented', prevented);
- }
- function testEnterInNonSetupBlockquoteWhenClassnameIsNotRequired() {
- setUpFields(false);
- resetChangeFlags();
- var prevented = !selectNodeAndHitEnter(field1, 'field1cursor');
- waitForChangeEvents();
- assertChangeFlags();
- // make sure there are two blockquotes, and a DIV with nbsp in the middle.
- var elem = field1.getElement();
- var dom = field1.getEditableDomHelper();
- assertEquals(
- 'Blockquote should be split', 2,
- dom.getElementsByTagNameAndClass(goog.dom.TagName.BLOCKQUOTE, null, elem)
- .length);
- assert(
- 'Selection should be deleted', -1 == elem.innerHTML.indexOf('selection'));
- assert(
- 'should have div with ',
- -1 != elem.innerHTML.indexOf('>' + getNbsp() + '<'));
- assert('event should have been prevented', prevented);
- }
- function testEnterInBlockquoteCreatesDivInBrMode() {
- setUpFields(true);
- selectNodeAndHitEnter(field2, 'field2cursor');
- var elem = field2.getElement();
- var dom = field2.getEditableDomHelper();
- var firstBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[0];
- var div = dom.getNextElementSibling(firstBlockquote);
- assertEquals('Element after blockquote should be a div', 'DIV', div.tagName);
- assertEquals(
- 'Element after div should be second blockquote', 'BLOCKQUOTE',
- dom.getNextElementSibling(div).tagName);
- }
- /**
- * Tests that breaking after a BR doesn't result in unnecessary newlines.
- * @bug 1471047
- */
- function testEnterInBlockquoteRemovesUnnecessaryBrWithCursorAfterBr() {
- setUpFields(true);
- // Assume the following HTML snippet:-
- // <blockquote>one<br>|two<br></blockquote>
- //
- // After enter on the cursor position without the fix, the resulting HTML
- // after the blockquote split was:-
- // <blockquote>one</blockquote>
- // <div> </div>
- // <blockquote><br>two<br></blockquote>
- //
- // This creates the impression on an unnecessary newline. The resulting HTML
- // after the fix is:-
- //
- // <blockquote>one<br></blockquote>
- // <div> </div>
- // <blockquote>two<br></blockquote>
- field1.setHtml(
- false, '<blockquote id="quote" class="tr_bq">one<br>' +
- 'two<br></blockquote>');
- var dom = field1.getEditableDomHelper();
- goog.dom.Range.createCaret(dom.getElement('quote'), 2).select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- var elem = field1.getElement();
- var secondBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[1];
- assertHTMLEquals('two<br>', secondBlockquote.innerHTML);
- // Verifies that a blockquote split doesn't happen if it doesn't need to.
- field1.setHtml(
- false, '<blockquote class="tr_bq">one<br id="brcursor"></blockquote>');
- selectNodeAndHitEnter(field1, 'brcursor');
- assertEquals(
- 1,
- dom.getElementsByTagNameAndClass(goog.dom.TagName.BLOCKQUOTE, null, elem)
- .length);
- }
- /**
- * Tests that breaking in a text node before a BR doesn't result in unnecessary
- * newlines.
- * @bug 1471047
- */
- function testEnterInBlockquoteRemovesUnnecessaryBrWithCursorBeforeBr() {
- setUpFields(true);
- // Assume the following HTML snippet:-
- // <blockquote>one|<br>two<br></blockquote>
- //
- // After enter on the cursor position, the resulting HTML should be.
- // <blockquote>one<br></blockquote>
- // <div> </div>
- // <blockquote>two<br></blockquote>
- field1.setHtml(
- false, '<blockquote id="quote" class="tr_bq">one<br>' +
- 'two<br></blockquote>');
- var dom = field1.getEditableDomHelper();
- var cursor = dom.getElement('quote').firstChild;
- goog.dom.Range.createCaret(cursor, 3).select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- var elem = field1.getElement();
- var secondBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[1];
- assertHTMLEquals('two<br>', secondBlockquote.innerHTML);
- // Ensures that standard text node split works as expected with the new
- // change.
- field1.setHtml(
- false, '<blockquote id="quote" class="tr_bq">one<b>two</b><br>');
- cursor = dom.getElement('quote').firstChild;
- goog.dom.Range.createCaret(cursor, 3).select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- secondBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[1];
- assertHTMLEquals('<b>two</b><br>', secondBlockquote.innerHTML);
- }
- /**
- * Tests that pressing enter in a blockquote doesn't create unnecessary
- * DOM subtrees.
- *
- * @bug 1991539
- * @bug 1991392
- */
- function testEnterInBlockquoteRemovesExtraNodes() {
- setUpFields(true);
- // Let's assume we have the following DOM structure and the
- // cursor is placed after the first numbered list item "one".
- //
- // <blockquote class="tr_bq">
- // <div><div>a</div><ol><li>one|</li></div>
- // <div>two</div>
- // </blockquote>
- //
- // After pressing enter, we have the following structure.
- //
- // <blockquote class="tr_bq">
- // <div><div>a</div><ol><li>one|</li></div>
- // </blockquote>
- // <div> </div>
- // <blockquote class="tr_bq">
- // <div><ol><li><span id=""></span></li></ol></div>
- // <div>two</div>
- // </blockquote>
- //
- // This appears to the user as an empty list. After the fix, the HTML
- // will be
- //
- // <blockquote class="tr_bq">
- // <div><div>a</div><ol><li>one|</li></div>
- // </blockquote>
- // <div> </div>
- // <blockquote class="tr_bq">
- // <div>two</div>
- // </blockquote>
- //
- field1.setHtml(
- false, '<blockquote class="tr_bq">' +
- '<div><div>a</div><ol><li id="cursor">one</li></div>' +
- '<div>b</div>' +
- '</blockquote>');
- var dom = field1.getEditableDomHelper();
- goog.dom.Range.createCaret(dom.getElement('cursor').firstChild, 3).select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- var elem = field1.getElement();
- var secondBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[1];
- assertHTMLEquals('<div>b</div>', secondBlockquote.innerHTML);
- // Ensure that we remove only unnecessary subtrees.
- field1.setHtml(
- false, '<blockquote class="tr_bq">' +
- '<div><span>a</span><div id="cursor">one</div><div>two</div></div>' +
- '<div><span>c</span></div>' +
- '</blockquote>');
- goog.dom.Range.createCaret(dom.getElement('cursor').firstChild, 3).select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- secondBlockquote = dom.getElementsByTagNameAndClass(
- goog.dom.TagName.BLOCKQUOTE, null, elem)[1];
- var expectedHTML = '<div><div>two</div></div>' +
- '<div><span>c</span></div>';
- assertHTMLEquals(expectedHTML, secondBlockquote.innerHTML);
- // Place the cursor in the middle of a line.
- field1.setHtml(
- false, '<blockquote id="quote" class="tr_bq">' +
- '<div>one</div><div>two</div>' +
- '</blockquote>');
- goog.dom.Range.createCaret(dom.getElement('quote').firstChild.firstChild, 1)
- .select();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- var blockquotes =
- dom.getElementsByTagNameAndClass(goog.dom.TagName.BLOCKQUOTE, null, elem);
- assertEquals(2, blockquotes.length);
- assertHTMLEquals('<div>o</div>', blockquotes[0].innerHTML);
- assertHTMLEquals('<div>ne</div><div>two</div>', blockquotes[1].innerHTML);
- }
- function testEnterInList() {
- setUpFields(true);
- // <enter> in a list should *never* be handled by custom code. Lists are
- // just way too complicated to get right.
- field1.setHtml(false, '<ol><li>hi!<span id="field1cursor"></span></li></ol>');
- if (goog.userAgent.OPERA) {
- // Opera doesn't actually place the selection in the empty span
- // unless we add a text node first.
- var dom = field1.getEditableDomHelper();
- dom.getElement('field1cursor').appendChild(dom.createTextNode(''));
- }
- var prevented = !selectNodeAndHitEnter(field1, 'field1cursor');
- assertFalse('<enter> in a list should not be prevented', prevented);
- }
- function testEnterAtEndOfBlockInWebkit() {
- setUpFields(true);
- if (goog.userAgent.WEBKIT) {
- field1.setHtml(
- false, '<blockquote>hi!<span id="field1cursor"></span></blockquote>');
- var cursor = field1.getEditableDomHelper().getElement('field1cursor');
- goog.editor.range.placeCursorNextTo(cursor, false);
- goog.dom.removeNode(cursor);
- var prevented = !goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- waitForChangeEvents();
- assertChangeFlags();
- assert('event should have been prevented', prevented);
- // Make sure that the block now has two brs.
- var elem = field1.getElement();
- assertEquals(
- 'should have inserted two br tags: ' + elem.innerHTML, 2,
- goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.BR, null, elem)
- .length);
- }
- }
- /**
- * Tests that deleting a BR that comes right before a block element works.
- * @bug 1471096
- * @bug 2056376
- */
- function testDeleteBrBeforeBlock() {
- setUpFields(true);
- // This test only works on Gecko, because it's testing for manual deletion of
- // BR tags, which is done only for Gecko. For other browsers we fall through
- // and let the browser do the delete, which can only be tested with a robot
- // test (see javascript/apps/editor/tests/delete_br_robot.html).
- if (goog.userAgent.GECKO) {
- field1.setHtml(false, 'one<br><br><div>two</div>');
- var helper = new goog.testing.editor.TestHelper(field1.getElement());
- helper.select(field1.getElement(), 2); // Between the two BR's.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted exactly one <br>', 'one<br><div>two</div>',
- field1.getElement().innerHTML);
- // We test the case where the BR has a previous sibling which is not
- // a block level element.
- field1.setHtml(false, 'one<br><ul><li>two</li></ul>');
- helper.select(field1.getElement(), 1); // Between one and BR.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted the <br>', 'one<ul><li>two</li></ul>',
- field1.getElement().innerHTML);
- // Verify that the cursor is placed at the end of the text node "one".
- var range = field1.getRange();
- var focusNode = range.getFocusNode();
- assertTrue('The selected range should be collapsed', range.isCollapsed());
- assertTrue(
- 'The focus node should be the text node "one"',
- focusNode.nodeType == goog.dom.NodeType.TEXT &&
- focusNode.data == 'one');
- assertEquals(
- 'The focus offset should be at the end of the text node "one"',
- focusNode.length, range.getFocusOffset());
- assertTrue(
- 'The next sibling of the focus node should be the UL',
- focusNode.nextSibling &&
- focusNode.nextSibling.tagName == goog.dom.TagName.UL);
- // We test the case where the previous sibling of the BR is a block
- // level element.
- field1.setHtml(false, '<div>foo</div><br><div><span>bar</span></div>');
- helper.select(field1.getElement(), 1); // Before the BR.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted the <br>',
- '<div>foo</div><div><span>bar</span></div>',
- field1.getElement().innerHTML);
- range = field1.getRange();
- assertEquals(
- 'The selected range should be contained within the <span>',
- String(goog.dom.TagName.SPAN), range.getContainerElement().tagName);
- assertTrue('The selected range should be collapsed', range.isCollapsed());
- // Verify that the cursor is placed inside the span at the beginning of bar.
- focusNode = range.getFocusNode();
- assertTrue(
- 'The focus node should be the text node "bar"',
- focusNode.nodeType == goog.dom.NodeType.TEXT &&
- focusNode.data == 'bar');
- assertEquals(
- 'The focus offset should be at the beginning ' +
- 'of the text node "bar"',
- 0, range.getFocusOffset());
- // We test the case where the BR does not have a previous sibling.
- field1.setHtml(false, '<br><ul><li>one</li></ul>');
- helper.select(field1.getElement(), 0); // Before the BR.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted the <br>', '<ul><li>one</li></ul>',
- field1.getElement().innerHTML);
- range = field1.getRange();
- // Verify that the cursor is placed inside the LI at the text node "one".
- assertEquals(
- 'The selected range should be contained within the <li>',
- String(goog.dom.TagName.LI), range.getContainerElement().tagName);
- assertTrue('The selected range should be collapsed', range.isCollapsed());
- focusNode = range.getFocusNode();
- assertTrue(
- 'The focus node should be the text node "one"',
- (focusNode.nodeType == goog.dom.NodeType.TEXT &&
- focusNode.data == 'one'));
- assertEquals(
- 'The focus offset should be at the beginning of ' +
- 'the text node "one"',
- 0, range.getFocusOffset());
- // Testing deleting a BR followed by a block level element and preceded
- // by a BR.
- field1.setHtml(false, '<br><br><ul><li>one</li></ul>');
- helper.select(field1.getElement(), 1); // Between the BR's.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted the <br>', '<br><ul><li>one</li></ul>',
- field1.getElement().innerHTML);
- // Verify that the cursor is placed inside the LI at the text node "one".
- range = field1.getRange();
- assertEquals(
- 'The selected range should be contained within the <li>',
- String(goog.dom.TagName.LI), range.getContainerElement().tagName);
- assertTrue('The selected range should be collapsed', range.isCollapsed());
- focusNode = range.getFocusNode();
- assertTrue(
- 'The focus node should be the text node "one"',
- (focusNode.nodeType == goog.dom.NodeType.TEXT &&
- focusNode.data == 'one'));
- assertEquals(
- 'The focus offset should be at the beginning of ' +
- 'the text node "one"',
- 0, range.getFocusOffset());
- } // End if GECKO
- }
- /**
- * Tests that deleting a BR before a blockquote doesn't remove quoted text.
- * @bug 1471075
- */
- function testDeleteBeforeBlockquote() {
- setUpFields(true);
- if (goog.userAgent.GECKO) {
- field1.setHtml(
- false, '<br><br><div><br><blockquote>foo</blockquote></div>');
- var helper = new goog.testing.editor.TestHelper(field1.getElement());
- helper.select(field1.getElement(), 0); // Before the first BR.
- // Fire three deletes in quick succession.
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted all the <br>\'s and the blockquote ' +
- 'isn\'t affected',
- '<div><blockquote>foo</blockquote></div>',
- field1.getElement().innerHTML);
- var range = field1.getRange();
- assertEquals(
- 'The selected range should be contained within the ' +
- '<blockquote>',
- String(goog.dom.TagName.BLOCKQUOTE), range.getContainerElement().tagName);
- assertTrue('The selected range should be collapsed', range.isCollapsed());
- var focusNode = range.getFocusNode();
- assertTrue(
- 'The focus node should be the text node "foo"',
- (focusNode.nodeType == goog.dom.NodeType.TEXT &&
- focusNode.data == 'foo'));
- assertEquals(
- 'The focus offset should be at the ' +
- 'beginning of the text node "foo"',
- 0, range.getFocusOffset());
- }
- }
- /**
- * Tests that deleting a BR is working normally (that the workaround for the
- * bug is not causing double deletes).
- * @bug 1471096
- */
- function testDeleteBrNormal() {
- setUpFields(true);
- // This test only works on Gecko, because it's testing for manual deletion of
- // BR tags, which is done only for Gecko. For other browsers we fall through
- // and let the browser do the delete, which can only be tested with a robot
- // test (see javascript/apps/editor/tests/delete_br_robot.html).
- if (goog.userAgent.GECKO) {
- field1.setHtml(false, 'one<br><br><br>two');
- var helper = new goog.testing.editor.TestHelper(field1.getElement());
- helper.select(
- field1.getElement(), 2); // Between the first and second BR's.
- field1.getElement().focus();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.DELETE);
- assertEquals(
- 'Should have deleted exactly one <br>', 'one<br><br>two',
- field1.getElement().innerHTML);
- } // End if GECKO
- }
- /**
- * Tests that deleteCursorSelectionW3C_ correctly recognizes visually
- * collapsed selections in Opera even if they contain a <br>.
- * See the deleteCursorSelectionW3C_ comment in enterhandler.js.
- */
- function testCollapsedSelectionKeepsBrOpera() {
- setUpFields(true);
- if (goog.userAgent.OPERA) {
- field1.setHtml(false, '<div><br id="pleasedontdeleteme"></div>');
- field1.focus();
- goog.testing.events.fireKeySequence(
- field1.getElement(), goog.events.KeyCodes.ENTER);
- assertNotNull(
- 'The <br> must not have been deleted',
- goog.dom.getElement('pleasedontdeleteme'));
- }
- }
- /**
- * Selects the node at the given id, and simulates an ENTER keypress.
- * @param {goog.editor.Field} field The field with the node.
- * @param {string} id A DOM id.
- * @return {boolean} Whether preventDefault was called on the event.
- */
- function selectNodeAndHitEnter(field, id) {
- var dom = field.getEditableDomHelper();
- var cursor = dom.getElement(id);
- goog.dom.Range.createFromNodeContents(cursor).select();
- return goog.testing.events.fireKeySequence(
- cursor, goog.events.KeyCodes.ENTER);
- }
- /**
- * Creates a field with only the enter handler plugged in, for testing.
- * @param {string} id A DOM id.
- * @return {goog.editor.Field} A field.
- */
- function makeField(id, classnameRequiredToSplitBlockquote) {
- var field = new goog.editor.Field(id);
- field.registerPlugin(new goog.editor.plugins.EnterHandler());
- field.registerPlugin(
- new goog.editor.plugins.Blockquote(classnameRequiredToSplitBlockquote));
- goog.events.listen(
- field, goog.editor.Field.EventType.BEFORECHANGE, function() {
- // set the global flag that beforechange was fired.
- firedBeforeChange = true;
- });
- goog.events.listen(
- field, goog.editor.Field.EventType.DELAYEDCHANGE, function() {
- // set the global flag that delayed change was fired.
- firedDelayedChange = true;
- });
- return field;
- }
- /**
- * Reset all the global flags related to change events.
- */
- function resetChangeFlags() {
- waitForChangeEvents();
- firedBeforeChange = firedDelayedChange = false;
- }
- /**
- * Asserts that both change flags were fired since the last reset.
- */
- function assertChangeFlags() {
- assert('Beforechange should have fired', firedBeforeChange);
- assert('Delayedchange should have fired', firedDelayedChange);
- }
- /**
- * Wait for delayedchange to propagate.
- */
- function waitForChangeEvents() {
- clock.tick(
- goog.editor.Field.DELAYED_CHANGE_FREQUENCY +
- goog.editor.Field.CHANGE_FREQUENCY);
- }
- function getNbsp() {
- // On WebKit (pre-528) and Opera, shows up as its unicode character in
- // innerHTML under some circumstances.
- return (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528')) ||
- goog.userAgent.OPERA ?
- '\u00a0' :
- ' ';
- }
- function testPrepareContent() {
- setUpFields(true);
- assertPreparedContents('hi', 'hi');
- assertPreparedContents(
- goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? '<br>' : '', ' ');
- }
- /**
- * Assert that the prepared contents matches the expected.
- */
- function assertPreparedContents(expected, original) {
- assertEquals(
- expected,
- field1.reduceOp_(goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, original));
- }
- // UTILITY FUNCTION TESTS.
- function testDeleteW3CSimple() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML = '<div>abcd</div>';
- var range = goog.dom.Range.createFromNodes(
- container.firstChild.firstChild, 1, container.firstChild.firstChild, 3);
- range.select();
- goog.editor.plugins.EnterHandler.deleteW3cRange_(range);
- goog.testing.dom.assertHtmlContentsMatch('<div>ad</div>', container);
- }
- }
- function testDeleteW3CAll() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML = '<div>abcd</div>';
- var range = goog.dom.Range.createFromNodes(
- container.firstChild.firstChild, 0, container.firstChild.firstChild, 4);
- range.select();
- goog.editor.plugins.EnterHandler.deleteW3cRange_(range);
- goog.testing.dom.assertHtmlContentsMatch('<div> </div>', container);
- }
- }
- function testDeleteW3CPartialEnd() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML = '<div>ab</div><div>cd</div>';
- var range = goog.dom.Range.createFromNodes(
- container.firstChild.firstChild, 1, container.lastChild.firstChild, 1);
- range.select();
- goog.editor.plugins.EnterHandler.deleteW3cRange_(range);
- goog.testing.dom.assertHtmlContentsMatch('<div>ad</div>', container);
- }
- }
- function testDeleteW3CNonPartialEnd() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML = '<div>ab</div><div>cd</div>';
- var range = goog.dom.Range.createFromNodes(
- container.firstChild.firstChild, 1, container.lastChild.firstChild, 2);
- range.select();
- goog.editor.plugins.EnterHandler.deleteW3cRange_(range);
- goog.testing.dom.assertHtmlContentsMatch('<div>a</div>', container);
- }
- }
- function testIsInOneContainer() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML = '<div><br></div>';
- var div = container.firstChild;
- var range = goog.dom.Range.createFromNodes(div, 0, div, 1);
- range.select();
- assertTrue(
- 'Selection must be recognized as being in one container',
- goog.editor.plugins.EnterHandler.isInOneContainerW3c_(range));
- }
- }
- function testDeletingEndNodesWithNoNewLine() {
- if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
- container.innerHTML =
- 'a<div>b</div><div><br></div><div>c</div><div>d</div>';
- var range = goog.dom.Range.createFromNodes(
- container.childNodes[2], 0, container.childNodes[4].childNodes[0], 1);
- range.select();
- var newRange = goog.editor.plugins.EnterHandler.deleteW3cRange_(range);
- goog.testing.dom.assertHtmlContentsMatch('a<div>b</div>', container);
- assertTrue(newRange.isCollapsed());
- assertEquals(container, newRange.getStartNode());
- assertEquals(2, newRange.getStartOffset());
- }
- }
|