node_test.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.editor.nodeTest');
  15. goog.setTestOnly('goog.editor.nodeTest');
  16. goog.require('goog.array');
  17. goog.require('goog.dom');
  18. goog.require('goog.dom.NodeType');
  19. goog.require('goog.dom.TagName');
  20. goog.require('goog.editor.node');
  21. goog.require('goog.style');
  22. goog.require('goog.testing.ExpectedFailures');
  23. goog.require('goog.testing.dom');
  24. goog.require('goog.testing.jsunit');
  25. goog.require('goog.userAgent');
  26. var expectedFailures;
  27. var parentNode;
  28. var childNode1;
  29. var childNode2;
  30. var childNode3;
  31. var gChildWsNode1 = null;
  32. var gChildTextNode1 = null;
  33. var gChildNbspNode1 = null;
  34. var gChildMixedNode1 = null;
  35. var gChildWsNode2a = null;
  36. var gChildWsNode2b = null;
  37. var gChildTextNode3a = null;
  38. var gChildWsNode3 = null;
  39. var gChildTextNode3b = null;
  40. function setUpPage() {
  41. expectedFailures = new goog.testing.ExpectedFailures();
  42. parentNode = document.getElementById('parentNode');
  43. childNode1 = parentNode.childNodes[0];
  44. childNode2 = parentNode.childNodes[1];
  45. childNode3 = parentNode.childNodes[2];
  46. }
  47. function tearDown() {
  48. expectedFailures.handleTearDown();
  49. }
  50. function setUpDomTree() {
  51. gChildWsNode1 = document.createTextNode(' \t\r\n');
  52. gChildTextNode1 = document.createTextNode('Child node');
  53. gChildNbspNode1 = document.createTextNode('\u00a0');
  54. gChildMixedNode1 = document.createTextNode('Text\n plus\u00a0');
  55. gChildWsNode2a = document.createTextNode('');
  56. gChildWsNode2b = document.createTextNode(' ');
  57. gChildTextNode3a = document.createTextNode('I am a grand child');
  58. gChildWsNode3 = document.createTextNode(' \t \r \n');
  59. gChildTextNode3b = document.createTextNode('I am also a grand child');
  60. childNode3.appendChild(gChildTextNode3a);
  61. childNode3.appendChild(gChildWsNode3);
  62. childNode3.appendChild(gChildTextNode3b);
  63. childNode1.appendChild(gChildMixedNode1);
  64. childNode1.appendChild(gChildWsNode1);
  65. childNode1.appendChild(gChildNbspNode1);
  66. childNode1.appendChild(gChildTextNode1);
  67. childNode2.appendChild(gChildWsNode2a);
  68. childNode2.appendChild(gChildWsNode2b);
  69. document.body.appendChild(parentNode);
  70. }
  71. function tearDownDomTree() {
  72. goog.dom.removeChildren(childNode1);
  73. goog.dom.removeChildren(childNode2);
  74. goog.dom.removeChildren(childNode3);
  75. gChildWsNode1 = null;
  76. gChildTextNode1 = null;
  77. gChildNbspNode1 = null;
  78. gChildMixedNode1 = null;
  79. gChildWsNode2a = null;
  80. gChildWsNode2b = null;
  81. gChildTextNode3a = null;
  82. gChildWsNode3 = null;
  83. gChildTextNode3b = null;
  84. }
  85. function testGetCompatModeQuirks() {
  86. var quirksIfr = goog.dom.createElement(goog.dom.TagName.IFRAME);
  87. document.body.appendChild(quirksIfr);
  88. // Webkit used to default to standards mode, but fixed this in
  89. // Safari 4/Chrome 2, aka, WebKit 530.
  90. // Also IE10 fails here.
  91. // TODO(johnlenz): IE10+ inherit quirks mode from the owner document
  92. // according to:
  93. // http://msdn.microsoft.com/en-us/library/ff955402(v=vs.85).aspx
  94. // but this test shows different behavior for IE10 and 11. If we discover
  95. // that we care about quirks mode documents we should investigate
  96. // this failure.
  97. expectedFailures.expectFailureFor(
  98. (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530')) ||
  99. (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&
  100. !goog.userAgent.isVersionOrHigher('11')));
  101. expectedFailures.run(function() {
  102. assertFalse(
  103. 'Empty sourceless iframe is quirks mode, not standards mode',
  104. goog.editor.node.isStandardsMode(
  105. goog.dom.getFrameContentDocument(quirksIfr)));
  106. });
  107. document.body.removeChild(quirksIfr);
  108. }
  109. function testGetCompatModeStandards() {
  110. var standardsIfr = goog.dom.createElement(goog.dom.TagName.IFRAME);
  111. document.body.appendChild(standardsIfr);
  112. var doc = goog.dom.getFrameContentDocument(standardsIfr);
  113. doc.open();
  114. doc.write('<!DOCTYPE HTML><html><head></head><body>&nbsp;</body></html>');
  115. doc.close();
  116. assertTrue(
  117. 'Iframe with DOCTYPE written in is standards mode',
  118. goog.editor.node.isStandardsMode(doc));
  119. document.body.removeChild(standardsIfr);
  120. }
  121. /**
  122. * Creates a DOM tree and tests that getLeftMostLeaf returns proper node
  123. */
  124. function testGetLeftMostLeaf() {
  125. setUpDomTree();
  126. assertEquals(
  127. 'Should skip ws node', gChildMixedNode1,
  128. goog.editor.node.getLeftMostLeaf(parentNode));
  129. assertEquals(
  130. 'Should skip ws node', gChildMixedNode1,
  131. goog.editor.node.getLeftMostLeaf(childNode1));
  132. assertEquals(
  133. 'Has no non ws leaves', childNode2,
  134. goog.editor.node.getLeftMostLeaf(childNode2));
  135. assertEquals(
  136. 'Should return first child', gChildTextNode3a,
  137. goog.editor.node.getLeftMostLeaf(childNode3));
  138. assertEquals(
  139. 'Has no children', gChildTextNode1,
  140. goog.editor.node.getLeftMostLeaf(gChildTextNode1));
  141. tearDownDomTree();
  142. }
  143. /**
  144. * Creates a DOM tree and tests that getRightMostLeaf returns proper node
  145. */
  146. function testGetRightMostLeaf() {
  147. setUpDomTree();
  148. assertEquals(
  149. "Should return child3's rightmost child", gChildTextNode3b,
  150. goog.editor.node.getRightMostLeaf(parentNode));
  151. assertEquals(
  152. 'Should skip ws node', gChildTextNode1,
  153. goog.editor.node.getRightMostLeaf(childNode1));
  154. assertEquals(
  155. 'Has no non ws leaves', childNode2,
  156. goog.editor.node.getRightMostLeaf(childNode2));
  157. assertEquals(
  158. 'Should return last child', gChildTextNode3b,
  159. goog.editor.node.getRightMostLeaf(childNode3));
  160. assertEquals(
  161. 'Has no children', gChildTextNode1,
  162. goog.editor.node.getRightMostLeaf(gChildTextNode1));
  163. tearDownDomTree();
  164. }
  165. /**
  166. * Creates a DOM tree and tests that getFirstChild properly ignores
  167. * ignorable nodes
  168. */
  169. function testGetFirstChild() {
  170. setUpDomTree();
  171. assertNull(
  172. 'Has no none ws children', goog.editor.node.getFirstChild(childNode2));
  173. assertEquals(
  174. 'Should skip first child, as it is ws', gChildMixedNode1,
  175. goog.editor.node.getFirstChild(childNode1));
  176. assertEquals(
  177. 'Should just return first child', gChildTextNode3a,
  178. goog.editor.node.getFirstChild(childNode3));
  179. assertEquals(
  180. 'Should return first child', childNode1,
  181. goog.editor.node.getFirstChild(parentNode));
  182. assertNull(
  183. 'First child of a text node should return null',
  184. goog.editor.node.getFirstChild(gChildTextNode1));
  185. assertNull(
  186. 'First child of null should return null',
  187. goog.editor.node.getFirstChild(null));
  188. tearDownDomTree();
  189. }
  190. /**
  191. * Create a DOM tree and test that getLastChild properly ignores
  192. * ignorable nodes
  193. */
  194. function testGetLastChild() {
  195. setUpDomTree();
  196. assertNull(
  197. 'Has no none ws children', goog.editor.node.getLastChild(childNode2));
  198. assertEquals(
  199. 'Should skip last child, as it is ws', gChildTextNode1,
  200. goog.editor.node.getLastChild(childNode1));
  201. assertEquals(
  202. 'Should just return last child', gChildTextNode3b,
  203. goog.editor.node.getLastChild(childNode3));
  204. assertEquals(
  205. 'Should return last child', childNode3,
  206. goog.editor.node.getLastChild(parentNode));
  207. assertNull(
  208. 'Last child of a text node should return null',
  209. goog.editor.node.getLastChild(gChildTextNode1));
  210. assertNull(
  211. 'Last child of null should return null',
  212. goog.editor.node.getLastChild(gChildTextNode1));
  213. tearDownDomTree();
  214. }
  215. /**
  216. * Test if nodes that should be ignorable return false and nodes that should
  217. * not be ignored return true.
  218. */
  219. function testIsImportant() {
  220. var wsNode = document.createTextNode(' \t\r\n');
  221. assertFalse(
  222. 'White space node is ignorable', goog.editor.node.isImportant(wsNode));
  223. var textNode = document.createTextNode('Hello');
  224. assertTrue('Text node is important', goog.editor.node.isImportant(textNode));
  225. var nbspNode = document.createTextNode('\u00a0');
  226. assertTrue(
  227. 'Node with nbsp is important', goog.editor.node.isImportant(nbspNode));
  228. var imageNode = goog.dom.createElement(goog.dom.TagName.IMG);
  229. assertTrue(
  230. 'Image node is important', goog.editor.node.isImportant(imageNode));
  231. }
  232. /**
  233. * Test that isAllNonNbspWhiteSpace returns true if node contains only
  234. * whitespace that is not nbsp and false otherwise
  235. */
  236. function testIsAllNonNbspWhiteSpace() {
  237. var wsNode = document.createTextNode(' \t\r\n');
  238. assertTrue(
  239. 'String is all non nbsp',
  240. goog.editor.node.isAllNonNbspWhiteSpace(wsNode));
  241. var textNode = document.createTextNode('Hello');
  242. assertFalse(
  243. 'String should not be whitespace',
  244. goog.editor.node.isAllNonNbspWhiteSpace(textNode));
  245. var nbspNode = document.createTextNode('\u00a0');
  246. assertFalse(
  247. 'String has nbsp', goog.editor.node.isAllNonNbspWhiteSpace(nbspNode));
  248. }
  249. /**
  250. * Creates a DOM tree and Test that getPreviousSibling properly ignores
  251. * ignorable nodes
  252. */
  253. function testGetPreviousSibling() {
  254. setUpDomTree();
  255. assertNull(
  256. 'No previous sibling',
  257. goog.editor.node.getPreviousSibling(gChildTextNode3a));
  258. assertEquals(
  259. 'Should have text sibling', gChildTextNode3a,
  260. goog.editor.node.getPreviousSibling(gChildWsNode3));
  261. assertEquals(
  262. 'Should skip over white space sibling', gChildTextNode3a,
  263. goog.editor.node.getPreviousSibling(gChildTextNode3b));
  264. assertNull(
  265. 'No previous sibling',
  266. goog.editor.node.getPreviousSibling(gChildMixedNode1));
  267. assertEquals(
  268. 'Should have mixed text sibling', gChildMixedNode1,
  269. goog.editor.node.getPreviousSibling(gChildWsNode1));
  270. assertEquals(
  271. 'Should skip over white space sibling', gChildMixedNode1,
  272. goog.editor.node.getPreviousSibling(gChildNbspNode1));
  273. assertNotEquals(
  274. 'Should not move past ws and nbsp', gChildMixedNode1,
  275. goog.editor.node.getPreviousSibling(gChildTextNode1));
  276. assertEquals(
  277. 'Should go to child 2', childNode2,
  278. goog.editor.node.getPreviousSibling(childNode3));
  279. assertEquals(
  280. 'Should go to child 1', childNode1,
  281. goog.editor.node.getPreviousSibling(childNode2));
  282. assertNull(
  283. 'Only has white space siblings',
  284. goog.editor.node.getPreviousSibling(gChildWsNode2b));
  285. tearDownDomTree();
  286. }
  287. /**
  288. * Creates a DOM tree and tests that getNextSibling properly ignores igrnorable
  289. * nodes when determining the next sibling
  290. */
  291. function testGetNextSibling() {
  292. setUpDomTree();
  293. assertEquals(
  294. 'Child 1 should have Child 2', childNode2,
  295. goog.editor.node.getNextSibling(childNode1));
  296. assertEquals(
  297. 'Child 2 should have child 3', childNode3,
  298. goog.editor.node.getNextSibling(childNode2));
  299. assertNull(
  300. 'Child 3 has no next sibling',
  301. goog.editor.node.getNextSibling(childNode3));
  302. assertNotEquals(
  303. 'Should not skip ws and nbsp nodes', gChildTextNode1,
  304. goog.editor.node.getNextSibling(gChildMixedNode1));
  305. assertNotEquals(
  306. 'Should not skip nbsp node', gChildTextNode1,
  307. goog.editor.node.getNextSibling(gChildWsNode1));
  308. assertEquals(
  309. 'Should have sibling', gChildTextNode1,
  310. goog.editor.node.getNextSibling(gChildNbspNode1));
  311. assertNull(
  312. 'Should have no next sibling',
  313. goog.editor.node.getNextSibling(gChildTextNode1));
  314. assertNull(
  315. 'Only has ws sibling', goog.editor.node.getNextSibling(gChildWsNode2a));
  316. assertNull(
  317. 'Has no next sibling', goog.editor.node.getNextSibling(gChildWsNode2b));
  318. assertEquals(
  319. 'Should skip ws node', gChildTextNode3b,
  320. goog.editor.node.getNextSibling(gChildTextNode3a));
  321. tearDownDomTree();
  322. }
  323. function testIsEmpty() {
  324. var textNode = document.createTextNode('');
  325. assertTrue(
  326. 'Text node with no content should be empty',
  327. goog.editor.node.isEmpty(textNode));
  328. textNode.data = '\xa0';
  329. assertTrue(
  330. 'Text node with nbsp should be empty',
  331. goog.editor.node.isEmpty(textNode));
  332. assertFalse(
  333. 'Text node with nbsp should not be empty when prohibited',
  334. goog.editor.node.isEmpty(textNode, true));
  335. textNode.data = ' ';
  336. assertTrue(
  337. 'Text node with whitespace should be empty',
  338. goog.editor.node.isEmpty(textNode));
  339. textNode.data = 'notEmpty';
  340. assertFalse(
  341. 'Text node with text should not be empty',
  342. goog.editor.node.isEmpty(textNode));
  343. var div = goog.dom.createElement(goog.dom.TagName.DIV);
  344. assertTrue('Empty div should be empty', goog.editor.node.isEmpty(div));
  345. div.innerHTML = '<iframe></iframe>';
  346. assertFalse(
  347. 'Div containing an iframe is not empty', goog.editor.node.isEmpty(div));
  348. div.innerHTML = '<img></img>';
  349. assertFalse(
  350. 'Div containing an image is not empty', goog.editor.node.isEmpty(div));
  351. div.innerHTML = '<embed></embed>';
  352. assertFalse(
  353. 'Div containing an embed is not empty', goog.editor.node.isEmpty(div));
  354. div.innerHTML = '<div><span></span></div>';
  355. assertTrue(
  356. 'Div containing other empty tags is empty',
  357. goog.editor.node.isEmpty(div));
  358. div.innerHTML = '<div><span> </span></div>';
  359. assertTrue(
  360. 'Div containing other empty tags and whitespace is empty',
  361. goog.editor.node.isEmpty(div));
  362. div.innerHTML = '<div><span>Not empty</span></div>';
  363. assertFalse(
  364. 'Div containing tags and text is not empty',
  365. goog.editor.node.isEmpty(div));
  366. var img = goog.dom.createElement(goog.dom.TagName.IMG);
  367. assertFalse('Empty img should not be empty', goog.editor.node.isEmpty(img));
  368. var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);
  369. assertFalse(
  370. 'Empty iframe should not be empty', goog.editor.node.isEmpty(iframe));
  371. var embed = goog.dom.createElement(goog.dom.TagName.EMBED);
  372. assertFalse(
  373. 'Empty embed should not be empty', goog.editor.node.isEmpty(embed));
  374. }
  375. /**
  376. * Test that getLength returns 0 if the node has no length and no children,
  377. * the # of children if the node has no length but does have children,
  378. * and the length of the node if the node does have length
  379. */
  380. function testGetLength() {
  381. var parentNode = goog.dom.createElement(goog.dom.TagName.P);
  382. assertEquals(
  383. 'Length 0 and no children', 0, goog.editor.node.getLength(parentNode));
  384. var childNode1 = document.createTextNode('node 1');
  385. var childNode2 = document.createTextNode('node number 2');
  386. var childNode3 = document.createTextNode('');
  387. parentNode.appendChild(childNode1);
  388. parentNode.appendChild(childNode2);
  389. parentNode.appendChild(childNode3);
  390. assertEquals(
  391. 'Length 0 and 3 children', 3, goog.editor.node.getLength(parentNode));
  392. assertEquals(
  393. 'Text node, length 6', 6, goog.editor.node.getLength(childNode1));
  394. assertEquals(
  395. 'Text node, length 0', 0, goog.editor.node.getLength(childNode3));
  396. }
  397. function testFindInChildrenSuccess() {
  398. var parentNode = goog.dom.createElement(goog.dom.TagName.DIV);
  399. parentNode.innerHTML = '<div>foo</div><b>foo2</b>';
  400. var index = goog.editor.node.findInChildren(parentNode, function(node) {
  401. return node.tagName == goog.dom.TagName.B;
  402. });
  403. assertEquals('Should find second child', index, 1);
  404. }
  405. function testFindInChildrenFailure() {
  406. var parentNode = goog.dom.createElement(goog.dom.TagName.DIV);
  407. parentNode.innerHTML = '<div>foo</div><b>foo2</b>';
  408. var index = goog.editor.node.findInChildren(
  409. parentNode, function(node) { return false; });
  410. assertNull("Shouldn't find a child", index);
  411. }
  412. function testFindHighestMatchingAncestor() {
  413. setUpDomTree();
  414. var predicateFunc = function(node) {
  415. return node.tagName == goog.dom.TagName.DIV;
  416. };
  417. var node = goog.editor.node.findHighestMatchingAncestor(
  418. gChildTextNode3a, predicateFunc);
  419. assertNotNull('Should return an ancestor', node);
  420. assertEquals(
  421. 'Should have found "parentNode" as the last ' +
  422. 'ancestor matching the predicate',
  423. parentNode, node);
  424. predicateFunc = function(node) { return node.childNodes.length == 1; };
  425. node = goog.editor.node.findHighestMatchingAncestor(
  426. gChildTextNode3a, predicateFunc);
  427. assertNull("Shouldn't return an ancestor", node);
  428. tearDownDomTree();
  429. }
  430. function testIsBlock() {
  431. var blockDisplays = [
  432. 'block', 'list-item', 'table', 'table-caption', 'table-cell',
  433. 'table-column', 'table-column-group', 'table-footer', 'table-footer-group',
  434. 'table-header-group', 'table-row', 'table-row-group'
  435. ];
  436. var structuralTags = [
  437. goog.dom.TagName.BODY, goog.dom.TagName.FRAME, goog.dom.TagName.FRAMESET,
  438. goog.dom.TagName.HEAD, goog.dom.TagName.HTML
  439. ];
  440. // The following tags are considered inline in IE, except LEGEND which is
  441. // only a block element in WEBKIT.
  442. var ambiguousTags = [
  443. goog.dom.TagName.DETAILS, goog.dom.TagName.HR, goog.dom.TagName.ISINDEX,
  444. goog.dom.TagName.LEGEND, goog.dom.TagName.MAP, goog.dom.TagName.NOFRAMES,
  445. goog.dom.TagName.OPTGROUP, goog.dom.TagName.OPTION, goog.dom.TagName.SUMMARY
  446. ];
  447. // Older versions of IE and Gecko consider the following elements to be
  448. // inline, but IE9+ and Gecko 2.0+ recognize the new elements.
  449. var legacyAmbiguousTags = [
  450. goog.dom.TagName.ARTICLE, goog.dom.TagName.ASIDE,
  451. goog.dom.TagName.FIGCAPTION, goog.dom.TagName.FIGURE,
  452. goog.dom.TagName.FOOTER, goog.dom.TagName.HEADER, goog.dom.TagName.HGROUP,
  453. goog.dom.TagName.NAV, goog.dom.TagName.SECTION
  454. ];
  455. var tagsToIgnore = goog.array.flatten(structuralTags, ambiguousTags);
  456. if ((goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) ||
  457. (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('2'))) {
  458. goog.array.extend(tagsToIgnore, legacyAmbiguousTags);
  459. }
  460. // Appending an applet tag can cause the test to hang if Java is blocked on
  461. // the system.
  462. tagsToIgnore.push(goog.dom.TagName.APPLET);
  463. // Appending an embed tag to the page in IE brings up a warning dialog about
  464. // loading Java content.
  465. if (goog.userAgent.IE) {
  466. tagsToIgnore.push(goog.dom.TagName.EMBED);
  467. }
  468. for (var tag in goog.dom.TagName) {
  469. if (goog.array.contains(tagsToIgnore, goog.dom.TagName[tag])) {
  470. continue;
  471. }
  472. var el = goog.dom.createElement(tag);
  473. document.body.appendChild(el);
  474. var display = goog.style.getCascadedStyle(el, 'display') ||
  475. goog.style.getComputedStyle(el, 'display');
  476. goog.dom.removeNode(el);
  477. if (goog.editor.node.isBlockTag(el)) {
  478. assertContains(
  479. 'Display for ' + tag + ' should be block-like', display,
  480. blockDisplays);
  481. } else {
  482. assertNotContains(
  483. 'Display for ' + tag + ' should not be block-like', display,
  484. blockDisplays);
  485. }
  486. }
  487. }
  488. function createDivWithTextNodes(var_args) {
  489. var dom = goog.dom.createDom(goog.dom.TagName.DIV);
  490. for (var i = 0; i < arguments.length; i++) {
  491. goog.dom.appendChild(dom, goog.dom.createTextNode(arguments[i]));
  492. }
  493. return dom;
  494. }
  495. function testSkipEmptyTextNodes() {
  496. assertNull(
  497. 'skipEmptyTextNodes should gracefully handle null',
  498. goog.editor.node.skipEmptyTextNodes(null));
  499. var dom1 = createDivWithTextNodes('abc', '', 'xyz', '', '');
  500. assertEquals(
  501. 'expected not to skip first child', dom1.firstChild,
  502. goog.editor.node.skipEmptyTextNodes(dom1.firstChild));
  503. assertEquals(
  504. 'expected to skip second child', dom1.childNodes[2],
  505. goog.editor.node.skipEmptyTextNodes(dom1.childNodes[1]));
  506. assertNull(
  507. 'expected to skip all the rest of the children',
  508. goog.editor.node.skipEmptyTextNodes(dom1.childNodes[3]));
  509. }
  510. function testIsEditableContainer() {
  511. var editableContainerElement = document.getElementById('editableTest');
  512. assertTrue(
  513. 'Container element should be considered editable container',
  514. goog.editor.node.isEditableContainer(editableContainerElement));
  515. var nonEditableContainerElement = document.getElementById('parentNode');
  516. assertFalse(
  517. 'Other element should not be considered editable container',
  518. goog.editor.node.isEditableContainer(nonEditableContainerElement));
  519. }
  520. function testIsEditable() {
  521. var editableContainerElement = document.getElementById('editableTest');
  522. var childNode = editableContainerElement.firstChild;
  523. var childElement = goog.dom.getElementsByTagName(
  524. goog.dom.TagName.SPAN, editableContainerElement)[0];
  525. assertFalse(
  526. 'Container element should not be considered editable',
  527. goog.editor.node.isEditable(editableContainerElement));
  528. assertTrue(
  529. 'Child text node should be considered editable',
  530. goog.editor.node.isEditable(childNode));
  531. assertTrue(
  532. 'Child element should be considered editable',
  533. goog.editor.node.isEditable(childElement));
  534. assertTrue(
  535. 'Grandchild node should be considered editable',
  536. goog.editor.node.isEditable(childElement.firstChild));
  537. assertFalse(
  538. 'Other element should not be considered editable',
  539. goog.editor.node.isEditable(document.getElementById('parentNode')));
  540. }
  541. function testFindTopMostEditableAncestor() {
  542. var root = document.getElementById('editableTest');
  543. var span = goog.dom.getElementsByTagName(goog.dom.TagName.SPAN, root)[0];
  544. var textNode = span.firstChild;
  545. assertEquals(
  546. 'Should return self if self is matched.', textNode,
  547. goog.editor.node.findTopMostEditableAncestor(textNode, function(node) {
  548. return node.nodeType == goog.dom.NodeType.TEXT;
  549. }));
  550. assertEquals(
  551. 'Should not walk out of editable node.', null,
  552. goog.editor.node.findTopMostEditableAncestor(textNode, function(node) {
  553. return node.tagName == goog.dom.TagName.BODY;
  554. }));
  555. assertEquals(
  556. 'Should not match editable container.', null,
  557. goog.editor.node.findTopMostEditableAncestor(textNode, function(node) {
  558. return node.tagName == goog.dom.TagName.DIV;
  559. }));
  560. assertEquals(
  561. 'Should find node in editable container.', span,
  562. goog.editor.node.findTopMostEditableAncestor(textNode, function(node) {
  563. return node.tagName == goog.dom.TagName.SPAN;
  564. }));
  565. }
  566. function testSplitDomTreeAt() {
  567. var innerHTML = '<p>1<b>2</b>3</p>';
  568. var root = goog.dom.createElement(goog.dom.TagName.DIV);
  569. root.innerHTML = innerHTML;
  570. var result = goog.editor.node.splitDomTreeAt(
  571. goog.dom.getElementsByTagName(goog.dom.TagName.B, root)[0], null, root);
  572. goog.testing.dom.assertHtmlContentsMatch('<p>1<b>2</b></p>', root);
  573. goog.testing.dom.assertHtmlContentsMatch('<p>3</p>', result);
  574. root.innerHTML = innerHTML;
  575. result = goog.editor.node.splitDomTreeAt(
  576. goog.dom.getElementsByTagName(goog.dom.TagName.B, root)[0],
  577. goog.dom.createTextNode('and'), root);
  578. goog.testing.dom.assertHtmlContentsMatch('<p>1<b>2</b></p>', root);
  579. goog.testing.dom.assertHtmlContentsMatch('<p>and3</p>', result);
  580. }
  581. function testTransferChildren() {
  582. var prefix = '<b>Bold 1</b>';
  583. var innerHTML = '<b>Bold</b><ul><li>Item 1</li><li>Item 2</li></ul>';
  584. var root1 = goog.dom.createElement(goog.dom.TagName.DIV);
  585. root1.innerHTML = innerHTML;
  586. var root2 = goog.dom.createElement(goog.dom.TagName.P);
  587. root2.innerHTML = prefix;
  588. var b = goog.dom.getElementsByTagName(goog.dom.TagName.B, root1)[0];
  589. // Transfer the children.
  590. goog.editor.node.transferChildren(root2, root1);
  591. assertEquals(0, root1.childNodes.length);
  592. goog.testing.dom.assertHtmlContentsMatch(prefix + innerHTML, root2);
  593. assertEquals(b, goog.dom.getElementsByTagName(goog.dom.TagName.B, root2)[1]);
  594. // Transfer them back.
  595. goog.editor.node.transferChildren(root1, root2);
  596. assertEquals(0, root2.childNodes.length);
  597. goog.testing.dom.assertHtmlContentsMatch(prefix + innerHTML, root1);
  598. assertEquals(b, goog.dom.getElementsByTagName(goog.dom.TagName.B, root1)[1]);
  599. }