Represent empty wlxml nodes as DocumentNodeElement with empty DocumentTextElement
[fnpeditor.git] / modules / documentCanvas / canvas / canvas.test3.js
1 define([
2 'libs/chai',
3 'libs/sinon',
4 'modules/documentCanvas/canvas/canvas',
5 'modules/documentCanvas/canvas/documentElement',
6 'modules/documentCanvas/canvas/utils'
7 ], function(chai, sinon, canvas, documentElement, utils) {
8     
9 'use strict';
10
11 var expect = chai.expect;
12
13
14 describe('Canvas', function() {
15
16     describe('Internal HTML representation of a DocumentNodeElement', function() {
17         it('is always a div tag', function() {
18             ['section', 'header', 'span', 'aside', 'figure'].forEach(function(tagName) {
19                 var dom = canvas.fromXML('<' + tagName + '></' + tagName + '>').doc().dom();
20                 expect(dom.prop('tagName')).to.equal('DIV', tagName + ' is represented as div');
21             });
22         });
23         it('has wlxml tag put into wlxml-tag attribute of its internal container', function() {
24             var dom = canvas.fromXML('<section></section>').doc().dom();
25             expect(dom.children('[document-element-content]').attr('wlxml-tag')).to.equal('section');
26         });
27         it('has wlxml class put into wlxml-class attribute of its internal containr, dots replaced with dashes', function() {
28             var dom = canvas.fromXML('<section class="some.class"></section>').doc().dom();
29             expect(dom.children('[document-element-content]').attr('wlxml-class')).to.equal('some-class');
30         });
31     });
32
33     describe('Internal HTML representation of a DocumentTextElement', function() {
34         it('is text node wrapped in a div with document-text-element attribute set', function() {
35             var dom = canvas.fromXML('<section>Alice</section>').doc().children()[0].dom();
36             expect(dom.prop('tagName')).to.equal('DIV');
37             expect(dom.attr('document-text-element')).to.equal('');
38             expect(dom.contents().length).to.equal(1);
39             expect(dom.contents()[0].nodeType).to.equal(Node.TEXT_NODE);
40             expect($(dom.contents()[0]).text()).to.equal('Alice');
41         });
42     });
43
44     describe('basic properties', function() {
45         it('renders empty document when canvas created from empty XML', function() {
46             var c = canvas.fromXML('');
47             expect(c.doc()).to.equal(null);
48         });
49
50         it('gives access to its document root node', function() {
51             var c = canvas.fromXML('<section></section>');
52             expect(c.doc().getWlxmlTag()).to.equal('section');
53         });
54
55         describe('root element', function() {
56             it('has no parent', function() {
57                 var c = canvas.fromXML('<section></section>');
58                 expect(c.doc().parent()).to.be.null;
59             });
60         });
61
62         describe('DocumentTextElement', function() {
63             it('can have its content set', function() {
64                 var c = canvas.fromXML('<section>Alice</section>'),
65                     root = c.doc(),
66                     text = root.children()[0];
67                 
68                 text.setText('a cat');
69                 expect(root.children()[0].getText()).to.equal('a cat');
70             });
71         });
72
73         describe('DocumentNodeElement', function() {
74             it('knows index of its child', function() {
75                 var c = canvas.fromXML('<section><div></div><header></header><span></span></section>'),
76                     root = c.doc(),
77                     child = root.children()[1];
78                 expect(root.childIndex(child)).to.equal(1);
79             });
80
81             it('knows WLXML tag it renders', function(){
82                 var c = canvas.fromXML('<section></section>'),
83                     section = c.doc();
84                 expect(section.getWlxmlTag()).to.equal('section', 'initial tag is section');
85                 section.setWlxmlTag('header');
86                 expect(section.getWlxmlTag()).to.equal('header', 'tag is changed to header');
87             });
88
89             it('knows WLXML class of a WLXML tag it renders', function(){
90                 var c = canvas.fromXML('<section class="some.class.A"></section>'),
91                     section = c.doc();
92                 expect(section.getWlxmlClass()).to.equal('some.class.A');
93                 section.setWlxmlClass('some.class.B');
94                 expect(section.getWlxmlClass()).to.equal('some.class.B');
95                 section.setWlxmlClass(null);
96                 expect(section.getWlxmlClass()).to.be.undefined;
97             });
98
99
100
101             describe('element has meta attributes', function() {
102                 it('can change its meta attributes', function() {
103                     var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
104                     span = c.doc().children()[0];
105                     
106                     expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
107                     span.setWlxmlMetaAttr('uri', 'otheruri');
108                     expect(span.getWlxmlMetaAttr('uri')).to.equal('otheruri');
109                 });
110
111                 it('changes its meta attributes with class change', function() {
112                     var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
113                     span = c.doc().children()[0];
114                     
115                     expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
116                     span.setWlxmlClass('author');
117                     expect(span.getWlxmlMetaAttr('uri')).to.be.undefined;
118                 });
119
120                 it('keeps meta attribute value on class change if a new class has this attribute', function() {
121                     var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
122                     span = c.doc().children()[0];
123                     span.setWlxmlClass('uri.some.subclass');
124                     expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
125                 });
126             });
127         });
128
129         it('returns DocumentNodeElement instance from HTMLElement', function() {
130             var c = canvas.fromXML('<section></section>'),
131                 htmlElement = c.doc().dom().get(0),
132                 element = c.getDocumentElement(htmlElement);
133             expect(element).to.be.instanceOf(documentElement.DocumentNodeElement);
134             expect(element.sameNode(c.doc()));
135         });
136         
137         it('returns DocumentTextElement instance from Text Node', function() {
138             var c = canvas.fromXML('<section>Alice</section>'),
139                 aliceElement = c.doc().children()[0],
140                 textNode = aliceElement.dom().contents()[0],
141                 element = c.getDocumentElement(textNode);
142
143             expect(textNode.nodeType).to.equal(Node.TEXT_NODE, 'text node selected');
144             expect($(textNode).text()).to.equal('Alice');
145
146             expect(element).to.be.instanceOf(documentElement.DocumentTextElement);
147             expect(element.sameNode(c.doc().children()[0]));
148         });
149     });
150
151
152
153     describe('document representation api', function() {
154         describe('document root element', function() {
155             var c = canvas.fromXML('<section></section>');
156             it('exists', function() {
157                 expect(c.doc()).to.be.instanceOf(documentElement.DocumentElement);
158             });
159             it('is of type DocumentNodeElement', function() {
160                 expect(c.doc()).to.be.instanceOf(documentElement.DocumentNodeElement);
161             });
162         });
163
164         describe('DocumentElements comparison', function() {
165             it('reports dwo DocumentElements to be the same when they represent the same wlxml document element', function() {
166                 var c = canvas.fromXML('<section><div></div><div></div></section>'),
167                     first_div1 = c.doc().children()[0],
168                     first_div2 = c.doc().children()[0],
169                     second_div = c.doc().children()[1];
170                 expect(first_div1.sameNode(first_div1)).to.be.true;
171                 expect(first_div1.sameNode(first_div2)).to.be.true;
172                 expect(first_div1.sameNode(second_div)).to.be.false;
173             });
174         });
175
176         describe('traversing', function() {
177             it('reports element nodes', function() {
178                 var c = canvas.fromXML('<section><div></div></section>'),
179                     children = c.doc().children();
180                 expect(children.length).to.equal(1);
181                 expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
182
183                 c = canvas.fromXML('<section><div></div><div></div></section>'),
184                     children = c.doc().children();
185                 expect(children.length).to.equal(2);
186                 expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
187                 expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
188             });
189             
190             it('reports text nodes', function() {
191                 var c = canvas.fromXML('<section>Alice</section>'),
192                     children = c.doc().children();
193                 expect(children.length).to.equal(1);
194                 expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
195             });
196
197             describe('accessing parents', function() {
198                 it('returns DocumentNodeElement representing parent in wlxml document as DocumentNodeElement parent', function() {
199                     var c = canvas.fromXML('<section><div></div></section>'),
200                         div = c.doc().children()[0];
201                     expect(div.parent().sameNode(c.doc())).to.be.true;
202                 });
203                 it('returns DocumentNodeElement representing parent in wlxml document as DocumentTextElement parent', function() {
204                     var c = canvas.fromXML('<section>Alice</section>'),
205                         text = c.doc().children()[0];
206                     expect(text.parent().sameNode(c.doc())).to.be.true;
207                 });
208             });
209
210             describe('accessing sibling parents of two elements', function() {
211                 it('returns elements themself if they have direct common parent', function() {
212                     var c = canvas.fromXML('<section>\
213                         <div>\
214                             <div>A</div>\
215                             <div>B</div>\
216                         </div>\
217                     </section>'),
218                         section = c.doc(),
219                         wrappingDiv = c.doc().children()[0],
220                         divA = wrappingDiv.children()[0],
221                         divB = wrappingDiv.children()[1];
222
223                     var siblingParents = c.getSiblingParents({element1: divA, element2: divB});
224
225                     expect(siblingParents.element1.sameNode(divA)).to.equal(true, 'divA');
226                     expect(siblingParents.element2.sameNode(divB)).to.equal(true, 'divB');
227                 });
228
229                 it('returns sibling parents - example 1', function() {
230                     var c = canvas.fromXML('<section>Alice <span>has a cat</span></section>'),
231                         section = c.doc(),
232                         aliceText = section.children()[0],
233                         span = section.children()[1],
234                         spanText = span.children()[0];
235
236                     var siblingParents = c.getSiblingParents({element1: aliceText, element2: spanText});
237
238                     expect(siblingParents.element1.sameNode(aliceText)).to.equal(true, 'aliceText');
239                     expect(siblingParents.element2.sameNode(span)).to.equal(true, 'span');
240                 });
241             })
242
243             describe('free text handling', function() {
244                     it('sees free text', function() {
245                         var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
246                             children = c.doc().children();
247                         expect(children.length).to.equal(3);
248                         expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
249                         expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
250                         expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
251                     });
252             });
253
254             describe('empty nodes handling', function() {
255                 it('says empty element node from XML source has one empty DocumentTextElement', function() {
256                     var c = canvas.fromXML('<section></section>');
257                     expect(c.doc().children()).to.have.length(1);
258                     expect(c.doc().children()[0].getText()).to.equal('');
259                 });
260
261                 it('allows creation of an empty element node', function() {
262                     var c = canvas.fromXML('<section></section>'),
263                         section = c.doc(),
264                         header = section.append({tag: 'header'});
265                     expect(header.children()).to.have.length(0);
266                 });
267             });
268             
269             describe('white characters handling', function() {
270
271                 it('says element node with one space has one DocumentTextElement', function() {
272                     var c = canvas.fromXML('<section> </section>');
273                     expect(c.doc().children().length).to.equal(1);
274                     expect(c.doc().children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
275                     expect(c.doc().children()[0].getText()).to.equal(' ');
276                 });
277                 it('ignores white space surrounding block elements', function() {
278                     var c = canvas.fromXML('<section> <div></div> </section>');
279                     var children = c.doc().children();
280                     expect(children.length).to.equal(1);
281                     expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
282                 });
283                 it('ignores white space between block elements', function() {
284                     var c = canvas.fromXML('<section><div></div> <div></div></section>');
285                     var children = c.doc().children();
286                     expect(children.length === 2);
287                     [0,1].forEach(function(idx) {
288                         expect(children[idx]).to.be.instanceOf(documentElement.DocumentNodeElement);
289                     });
290                 });
291
292                 it('trims white space from the beginning and the end of the block elements', function() {
293                     var c = canvas.fromXML('<section> Alice <span>has</span> a cat </section>');
294                     expect(c.doc().children()[0].getText()).to.equal('Alice ');
295                     expect(c.doc().children()[2].getText()).to.equal(' a cat');
296                 });
297
298                 it('normalizes string of white characters to one space at the inline element boundries', function() {
299                     var c = canvas.fromXML('<span>   Alice has a cat   </span>');
300                     expect(c.doc().children()[0].getText()).to.equal(' Alice has a cat ');
301                 });
302
303                 it('normalizes string of white characters to one space before inline element', function() {
304                     var c = canvas.fromXML('<div>Alice has  <span>a cat</span></div>');
305                     expect(c.doc().children()[0].getText()).to.equal('Alice has ');
306                 });
307                 
308                 it('normalizes string of white characters to one space after inline element', function() {
309                     var c = canvas.fromXML('<div>Alice has <span>a</span>  cat</div>');
310                     expect(c.doc().children()[2].getText()).to.equal(' cat');
311                 });
312             });
313
314             describe('getting vertically first text element', function() {
315                 it('returns the first child if it\'s text element, ignores metadata', function() {
316                     var c = canvas.fromXML('<section><metadata><dc:author>author</dc:author></metadata>Alice<div>has</div>a cat</section>'),
317                         first = c.doc().getVerticallyFirstTextElement();
318
319                     expect(first.sameNode(c.doc().children()[1])).to.be.true;
320                 });
321
322                 it('looks recursively inside node elements if they precede text element', function() {
323                     var c = canvas.fromXML('\
324                             <section>\
325                                 <div>\
326                                     <div>\
327                                         Alice\
328                                     </div>\
329                                 </div>\
330                                 Some text\
331                             </section>'),
332                         textAlice = c.doc().children()[0].children()[0].children()[0],
333                         first = c.doc().getVerticallyFirstTextElement();
334
335                     expect(textAlice).to.be.instanceOf(documentElement.DocumentTextElement);
336                     expect(first.sameNode(textAlice)).to.be.true;
337                 });
338             });
339         });
340
341         describe('manipulation api', function() {
342
343             describe('Basic Element inserting', function() {
344                 it('can put new NodeElement at the end', function() {
345                     var c = canvas.fromXML('<section><div></div></section>'),
346                         appended = c.doc().append({tag: 'header', klass: 'some.class'}),
347                         children = c.doc().children();
348
349                     expect(children.length).to.equal(2);
350                     expect(children[1].sameNode(appended)).to.be.true;
351                 });
352
353                 it('can put new TextElement at the end', function() {
354                     var c = canvas.fromXML('<section><div><div></section>'),
355                         appended = c.doc().append({text: 'Alice'}),
356                         children = c.doc().children();
357
358                     expect(children.length).to.equal(2);
359                     expect(children[1].sameNode(appended)).to.be.true;
360                     expect(children[1].getText()).to.equal('Alice');
361                 });
362
363                 it('can put new NodeElement at the beginning', function() {
364                     var c = canvas.fromXML('<section><div></div></section>'),
365                         prepended = c.doc().prepend({tag: 'header', klass: 'some.class'}),
366                         children = c.doc().children();
367
368                     expect(children).to.have.length(2);
369                     expect(children[0].sameNode(prepended)).to.be.true;
370                 });
371
372                 it('can put new TextElement at the beginning', function() {
373                     var c = canvas.fromXML('<section><div></div></section>'),
374                         prepended = c.doc().prepend({text: 'Alice'}),
375                         children = c.doc().children();
376
377                     expect(children).to.have.length(2)
378                     expect(children[0].sameNode(prepended)).to.be.true;
379                     expect(children[0].getText()).to.equal('Alice');
380                 });
381
382                 it('can put new NodeElement after another NodeElement', function() {
383                     var c = canvas.fromXML('<section><div></div></section>'),
384                         div = c.doc().children()[0],
385                         added = div.after({tag: 'header', klass: 'some.class'}),
386                         children = c.doc().children();
387                     expect(children.length).to.equal(2);
388                     expect(children[1].sameNode(added)).to.be.true;
389                 });
390
391                 it('can put new Nodeelement before another element', function() {
392                     var c = canvas.fromXML('<section><div></div></section>'),
393                         div = c.doc().children()[0],
394                         added = div.before({tag: 'header', klass: 'some.class'}),
395                         children = c.doc().children();
396                     expect(children.length).to.equal(2);
397                     expect(children[0].sameNode(added)).to.be.true;
398                 });
399
400                 it('can put new DocumentNodeElement after DocumentTextElement', function() {
401                     var c = canvas.fromXML('<section>Alice</section>'),
402                         text = c.doc().children()[0],
403                         added = text.after({tag: 'p'}),
404                         children = c.doc().children();
405
406                     expect(children.length).to.equal(2);
407                     expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
408                     expect(children[0].getText()).to.equal('Alice');
409                     expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
410                     expect(children[1].sameNode(added)).to.be.true;
411                 });
412                 it('can put new DocumentNodeElement before DocumentTextElement', function() {
413                     var c = canvas.fromXML('<section>Alice</section>'),
414                         text = c.doc().children()[0],
415                         added = text.before({tag: 'p'}),
416                         children = c.doc().children();
417
418                     expect(children.length).to.equal(2);
419                     expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
420                     expect(children[0].sameNode(added)).to.be.true;
421                     expect(children[1]).to.be.instanceOf(documentElement.DocumentTextElement);
422                     expect(children[1].getText()).to.equal('Alice');
423                 });
424
425                 it('can divide DocumentTextElement with a new DocumentNodeElement', function() {
426                     var c = canvas.fromXML('<section>Alice has a cat</section>'),
427                         section = c.doc(),
428                         text = section.children()[0];
429
430                     var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 5}),
431                         sectionChildren = section.children(),
432                         lhsText = sectionChildren[0],
433                         rhsText = sectionChildren[2];
434
435                     expect(lhsText.getText()).to.equal('Alice');
436                     expect(returned.sameNode(sectionChildren[1]));
437                     expect(rhsText.getText()).to.equal(' has a cat');
438                 });
439
440                 it('treats dividing DocumentTextElement at the very end as appending after it', function() {
441                     var c = canvas.fromXML('<section>Alice has a cat</section>'),
442                         section = c.doc(),
443                         text = section.children()[0];
444
445                     var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 15}),
446                         sectionChildren = section.children(),
447                         textElement = sectionChildren[0],
448                         nodeElement = sectionChildren[1];
449
450                     expect(sectionChildren.length).to.equal(2);
451                     expect(textElement.getText()).to.equal('Alice has a cat');
452                     expect(returned.sameNode(nodeElement)).to.be.true;
453                     expect(nodeElement.getWlxmlTag()).to.equal('aside');
454                 });
455
456                 it('treats dividing DocumentTextElement at the very beginning as appending before it', function() {
457                     var c = canvas.fromXML('<section>Alice has a cat</section>'),
458                         section = c.doc(),
459                         text = section.children()[0];
460
461                     var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 0}),
462                         sectionChildren = section.children(),
463                         nodeElement = sectionChildren[0],
464                         textElement = sectionChildren[1];
465                         
466                     expect(sectionChildren.length).to.equal(2);
467                     expect(textElement.getText()).to.equal('Alice has a cat');
468                     expect(returned.sameNode(nodeElement)).to.be.true;
469                     expect(nodeElement.getWlxmlTag()).to.equal('aside');
470                 });
471             });
472
473             describe('Removing elements', function() {
474                 it('merges left and right DocumentTextElement sibling of a detached DocumentNodeElement', function() {
475                     var c = canvas.fromXML('<section>Alice<div>has</div>a cat</section>'),
476                         section = c.doc(),
477                         div = section.children()[1];
478
479                     div.detach();
480
481                     var sectionChildren = section.children(),
482                         textElement = sectionChildren[0];
483
484                     expect(sectionChildren).to.have.length(1);
485                     expect(textElement.getText()).to.equal('Alicea cat');
486                 });
487             });
488
489             describe('Splitting text', function() {
490                 
491                 it('splits DocumentTextElement\'s parent into two DocumentNodeElements of the same type', function() {
492                     var c = canvas.fromXML('<section><header>Some header</header></section>'),
493                         section = c.doc(),
494                         text = section.children()[0].children()[0];
495
496                     var returnedValue = text.split({offset: 5});
497                     expect(section.children().length).to.equal(2, 'section has two children');
498                     
499                     var header1 = section.children()[0];
500                     var header2 = section.children()[1];
501
502                     expect(header1.getWlxmlTag()).to.equal('header', 'first section child represents wlxml header');
503                     expect(header1.children().length).to.equal(1, 'first header has one text child');
504                     expect(header1.children()[0].getText()).to.equal('Some ', 'first header has correct content');
505                     expect(header2.getWlxmlTag()).to.equal('header', 'second section child represents wlxml header');
506                     expect(header2.children().length).to.equal(1, 'second header has one text child');
507                     expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content');
508
509                     expect(returnedValue.first.sameNode(header1)).to.equal(true, 'first node returnde');
510                     expect(returnedValue.second.sameNode(header2)).to.equal(true, 'second node returned');
511                 });
512
513                 it('leaves empty copy of DocumentNodeElement if splitting at the very beginning', function() {
514                         var c = canvas.fromXML('<section><header>Some header</header></section>'),
515                         section = c.doc(),
516                         text = section.children()[0].children()[0];
517
518                         text.split({offset: 0});
519                         
520                         var header1 = section.children()[0];
521                         var header2 = section.children()[1];
522
523                         expect(header1.children().length).to.equal(0);
524                         expect(header2.children()[0].getText()).to.equal('Some header');
525                 });
526
527                 it('leaves empty copy of DocumentNodeElement if splitting at the very end', function() {
528                         var c = canvas.fromXML('<section><header>Some header</header></section>'),
529                         section = c.doc(),
530                         text = section.children()[0].children()[0];
531
532                         text.split({offset: 11});
533                         
534                         var header1 = section.children()[0];
535                         var header2 = section.children()[1];
536
537                         expect(header1.children()[0].getText()).to.equal('Some header');
538                         expect(header2.children().length).to.equal(0);
539                 });
540
541                 it('keeps DocumentTextElement\'s parent\'s children elements intact', function() {
542                     var c = canvas.fromXML('\
543                             <section>\
544                                 <header>\
545                                     A <span>fancy</span> and <span>nice</span> header\
546                                 </header>\
547                             </section>'),
548                         section = c.doc(),
549                         header = section.children()[0],
550                         textAnd = header.children()[2];
551
552                     textAnd.split({offset: 2});
553                     
554                     var sectionChildren = section.children();
555                     expect(sectionChildren.length).to.equal(2, 'Section has two children');
556                     expect(sectionChildren[0].getWlxmlTag()).to.equal('header', 'First section element is a wlxml header');
557                     expect(sectionChildren[1].getWlxmlTag()).to.equal('header', 'Second section element is a wlxml header');
558
559                     var firstHeaderChildren = sectionChildren[0].children();
560                     expect(firstHeaderChildren.length).to.equal(3, 'First header has three children');
561                     expect(firstHeaderChildren[0].getText()).to.equal('A ', 'First header starts with a text');
562                     expect(firstHeaderChildren[1].getWlxmlTag()).to.equal('span', 'First header has span in the middle');
563                     expect(firstHeaderChildren[2].getText()).to.equal(' a', 'First header ends with text');
564
565                     var secondHeaderChildren = sectionChildren[1].children();
566                     expect(secondHeaderChildren.length).to.equal(3, 'Second header has three children');
567                     expect(secondHeaderChildren[0].getText()).to.equal('nd ', 'Second header starts with text');
568                     expect(secondHeaderChildren[1].getWlxmlTag()).to.equal('span', 'Second header has span in the middle');
569                     expect(secondHeaderChildren[2].getText()).to.equal(' header', 'Second header ends with text');
570                 });
571             });
572
573             describe('wrapping', function() {
574                 it('wraps DocumentNodeElement', function() {
575                     var c = canvas.fromXML('<section><div></div></section>'),
576                         div = c.doc().children()[0];
577                     
578                     var returned = div.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
579                         parent = div.parent(),
580                         parent2 = c.doc().children()[0];
581
582                     expect(returned.sameNode(parent)).to.be.true;
583                     expect(returned.sameNode(parent2)).to.be.true;
584                     expect(returned.getWlxmlTag()).to.equal('header');
585                     expect(returned.getWlxmlClass()).to.equal('some.class');
586                 });
587                 it('wraps DocumentTextElement', function() {
588                     var c = canvas.fromXML('<section>Alice</section>'),
589                         text = c.doc().children()[0];
590                     
591                     var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
592                         parent = text.parent(),
593                         parent2 = c.doc().children()[0];
594
595                     expect(returned.sameNode(parent)).to.be.true;
596                     expect(returned.sameNode(parent2)).to.be.true;
597                     expect(returned.getWlxmlTag()).to.equal('header');
598                     expect(returned.getWlxmlClass()).to.equal('some.class');
599                 });
600                 
601                 describe('wrapping part of DocumentTextElement', function() {
602                     [{start: 5, end: 12}, {start: 12, end: 5}].forEach(function(offsets) {
603                         it('wraps in the middle ' + offsets.start + '/' + offsets.end, function() {
604                             var c = canvas.fromXML('<section>Alice has a cat</section>'),
605                                 text = c.doc().children()[0];
606                             
607                             var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: offsets.start, end: offsets.end}),
608                                 children = c.doc().children();
609
610                             expect(children.length).to.equal(3);
611                             
612                             expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
613                             expect(children[0].getText()).to.equal('Alice');
614
615                             expect(children[1].sameNode(returned)).to.be.true;
616                             expect(returned.getWlxmlTag()).to.equal('header');
617                             expect(returned.getWlxmlClass()).to.equal('some.class');
618                             expect(children[1].children().length).to.equal(1);
619                             expect(children[1].children()[0].getText()).to.equal(' has a ');
620
621                             expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
622                             expect(children[2].getText()).to.equal('cat');
623                         });
624                     });
625
626                     it('wraps whole text inside DocumentTextElement if offsets span entire content', function() {
627                          var c = canvas.fromXML('<section>Alice has a cat</section>'),
628                              text = c.doc().children()[0];
629                          
630                          var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 0, end: 15}),
631                              children = c.doc().children();
632
633                          expect(children.length).to.equal(1);
634                          expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
635                          expect(children[0].children()[0].getText()).to.equal('Alice has a cat');
636                     });
637                 });
638
639                 it('wraps text spanning multiple sibling DocumentTextNodes', function() {
640                     var c = canvas.fromXML('<section>Alice has a <span>small</span> cat</section>'),
641                         section = c.doc(),
642                         wrapper = c.wrapText({
643                             inside: section, 
644                             _with: {tag: 'span', klass: 'some.class'},
645                             offsetStart: 6,
646                             offsetEnd: 4,
647                             textNodeIdx: [0,2]
648                         });
649
650                     expect(section.children().length).to.equal(2);
651                     expect(section.children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
652                     expect(section.children()[0].getText()).to.equal('Alice ');
653
654                     var wrapper2 = section.children()[1];
655                     expect(wrapper2.sameNode(wrapper)).to.be.true;
656
657                     var wrapperChildren = wrapper.children();
658                     expect(wrapperChildren.length).to.equal(3);
659                     expect(wrapperChildren[0].getText()).to.equal('has a ');
660
661                     expect(wrapperChildren[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
662                     expect(wrapperChildren[1].children().length).to.equal(1);
663                     expect(wrapperChildren[1].children()[0].getText()).to.equal('small');
664
665                     expect(wrapperChildren[2].getText()).to.equal(' cat');
666                 });
667
668                 it('wraps multiple sibling Elements', function() {
669                     var c = canvas.fromXML('<section>Alice<div>has</div><div>a cat</div></section>'),
670                         section = c.doc(),
671                         aliceText = section.children()[0],
672                         firstDiv = section.children()[1],
673                         lastDiv = section.children()[section.children().length -1];
674
675                     var returned = c.wrapElements({
676                             element1: aliceText,
677                             element2: lastDiv,
678                             _with: {tag: 'header'}
679                         });
680
681                     var sectionChildren = section.children(),
682                         header = sectionChildren[0],
683                         headerChildren = header.children();
684
685                     expect(sectionChildren).to.have.length(1);
686                     expect(header.sameNode(returned)).to.equal(true, 'wrapper returned');
687                     expect(headerChildren).to.have.length(3);
688                     expect(headerChildren[0].sameNode(aliceText)).to.equal(true, 'first node wrapped');
689                     expect(headerChildren[1].sameNode(firstDiv)).to.equal(true, 'second node wrapped');
690                     expect(headerChildren[2].sameNode(lastDiv)).to.equal(true, 'third node wrapped');
691                 });
692                 it('wraps multiple sibling Elements - middle case', function() {
693                     var c = canvas.fromXML('<section><div></div>div></div><div></div><div></div></section>'),
694                         section = c.doc(),
695                         div1 = section.children()[0],
696                         div2 = section.children()[1],
697                         div3 = section.children()[2],
698                         div4 = section.children()[3];
699
700                     var returned = c.wrapElements({
701                             element1: div2,
702                             element2: div3,
703                             _with: {tag: 'header'}
704                         });
705
706                     var sectionChildren = section.children(),
707                         header = sectionChildren[1],
708                         headerChildren = header.children();
709
710                     expect(sectionChildren).to.have.length(3);
711                     expect(headerChildren).to.have.length(2);
712                     expect(headerChildren[0].sameNode(div2)).to.equal(true, 'first node wrapped');
713                     expect(headerChildren[1].sameNode(div3)).to.equal(true, 'second node wrapped');
714                 });
715             });
716
717             describe('unwrapping DocumentTextElement from its parent DocumentNodeElement if it\'s its only child', function() {
718                 it('unwraps text element from its parent and stays between its old parent siblings', function() {
719                     var c = canvas.fromXML('<section><div>Alice</div><div>has</div><div>a cat</div></section>'),
720                         section = c.doc(),
721                         sectionChildren = section.children(),
722                         divAlice = sectionChildren[0],
723                         divHas = sectionChildren[1],
724                         textHas = divHas.children()[0],
725                         divCat = sectionChildren[2];
726
727                     var newTextContainer = textHas.unwrap(),
728                         sectionChildren = section.children();
729
730                     expect(sectionChildren[0].sameNode(divAlice)).to.equal(true, 'divAlice ok');
731                     expect(newTextContainer.sameNode(section)).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
732                     expect(sectionChildren[1].getText()).to.equal('has');
733                     expect(sectionChildren[2].sameNode(divCat)).to.equal(true, 'divCat ok');
734
735                 });
736                 it('unwraps and join with its old parent adjacent text elements ', function() {
737                     var c = canvas.fromXML('<section>Alice <span>has a</span> cat</section>'),
738                     section = c.doc(),
739                     text = section.children()[1].children()[0];
740
741                     var newTextContainer = text.unwrap();
742
743                     expect(section.children().length).to.equal(1, 'section has one child');
744                     expect(section.children()[0].getText()).to.equal('Alice has a cat');
745                     expect(newTextContainer.sameNode(c.doc())).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
746                 });
747
748                 it('unwraps text element from its parent - first child case', function() {
749                     var c = canvas.fromXML('<section><span class="uri">Some</span>text</section>'),
750                         section = c.doc(),
751                         span = section.children()[0];
752
753                     span.children()[0].unwrap();
754
755                     var sectionChildren = section.children();
756
757                     expect(sectionChildren).to.have.length(1);
758                     expect(sectionChildren[0].getText()).to.equal('Sometext');
759                 });
760             });
761         });
762
763         describe('Lists api', function() {
764             describe('creating lists', function() {
765                 it('allows creation of a list from existing sibling DocumentElements', function() {
766                     var c = canvas.fromXML('\
767                         <section>\
768                             Alice\
769                             <div>has</div>\
770                             a\
771                             <div>cat</div>\
772                         </section>'),
773                         section = c.doc(),
774                         textHas = section.children()[1],
775                         divA = section.children()[2]
776                     
777                     c.list.create({element1: textHas, element2: divA});
778
779                     expect(section.children().length).to.equal(3, 'section has three child elements');
780
781                     var child1 = section.children()[0],
782                         list = section.children()[1],
783                         child3 = section.children()[2];
784
785                     expect(child1.getText()).to.equal('Alice');
786                     expect(list.is('list')).to.equal(true, 'second child is a list');
787                     expect(list.children().length).to.equal(2, 'list contains two elements');
788                     list.children().forEach(function(child) {
789                         expect(child.getWlxmlClass()).to.equal('item', 'list childs have wlxml class of item');
790                     });
791                     expect(child3.children()[0].getText()).to.equal('cat');
792                 });
793                 
794                 it('allows creating nested list from existing sibling list items', function() {
795                     var c = canvas.fromXML('\
796                         <section>\
797                             <div class="list-items">\
798                                 <div class="item">A</div>\
799                                 <div class="item">B</div>\
800                                 <div class="item">C</div>\
801                                 <div class="item">D</div>\
802                             </div>\
803                         </section>'),
804                         outerList = c.doc().children()[0],
805                         itemB = outerList.children()[1],
806                         itemC = outerList.children()[2];
807
808
809                         c.list.create({element1: itemB, element2: itemC});
810
811                     var outerListItems = outerList.children(),
812                         innerList = outerListItems[1].children()[0],
813                         innerListItems = innerList.children();
814
815                     expect(outerListItems.length).to.equal(3, 'outer list has three items');
816                     expect(outerListItems[0].children()[0].getText()).to.equal('A', 'first outer item ok');
817                     expect(outerListItems[1].getWlxmlClass()).to.equal('item', 'inner list is wrapped by item element');
818
819                     expect(innerList.is('list')).to.equal(true, 'inner list created');
820                     expect(innerListItems.length).to.equal(2, 'inner list has two items');
821                     expect(innerListItems[0].children()[0].getText()).to.equal('B', 'first inner item ok');
822                     expect(innerListItems[1].children()[0].getText()).to.equal('C', 'second inner item ok');
823
824                     expect(outerListItems[2].children()[0].getText()).to.equal('D', 'last outer item ok');
825
826                 });
827
828             });
829
830             describe('extracting list items', function() {
831                 it('creates two lists with extracted items in the middle if extracting from the middle of the list', function() {
832                     var c = canvas.fromXML('\
833                         <section>\
834                             <div class="list.items">\
835                                 <div class="item">0</div>\
836                                 <div class="item">1</div>\
837                                 <div class="item">2</div>\
838                                 <div class="item">3</div>\
839                             </div>\
840                         </section>'),
841                         list = c.doc().children()[0],
842                         item1 = list.children()[1],
843                         item2 = list.children()[2];
844
845                     c.list.extractItems({element1: item1, element2: item2});
846
847                     var section = c.doc(),
848                         list1 = section.children()[0],
849                         oldItem1 = section.children()[1],
850                         oldItem2 = section.children()[2],
851                         list2 = section.children()[3];
852
853                     expect(section.children().length).to.equal(4, 'section contains four children');
854                     
855                     expect(list1.is('list')).to.equal(true, 'first section child is a list');
856                     expect(list1.children().length).to.equal(1, 'first list has one child');
857                     expect(list1.children()[0].children()[0].getText()).to.equal('0', 'first item of the first list is a first item of the original list');
858
859                     expect(oldItem1.children()[0].getText()).to.equal('1', 'first item got extracted');
860                     expect(oldItem1.getWlxmlClass() === undefined).to.equal(true, 'first extracted element has no wlxml class');
861
862                     expect(oldItem2.children()[0].getText()).to.equal('2', 'second item got extracted');
863                     expect(oldItem2.getWlxmlClass() === undefined).to.equal(true, 'second extracted element has no wlxml class');
864
865                     expect(list2.is('list')).to.equal(true, 'last section child is a list');
866                     expect(list2.children().length).to.equal(1, 'second list has one child');
867                     expect(list2.children()[0].children()[0].getText()).to.equal('3', 'first item of the second list is a last item of the original list');
868                 });
869
870                 it('puts extracted items above the list if starting item is the first one', function() {
871                     var c = canvas.fromXML('\
872                         <section>\
873                             <div class="list.items">\
874                                 <div class="item">0</div>\
875                                 <div class="item">1</div>\
876                                 <div class="item">2</div>\
877                             </div>\
878                         </section>'),
879                         list = c.doc().children()[0],
880                         item1 = list.children()[0],
881                         item2 = list.children()[1],
882                         item3 = list.children()[2];
883
884                     c.list.extractItems({element1: item1, element2: item2});
885
886                     var section = c.doc(),
887                         oldItem1 = section.children()[0],
888                         oldItem2 = section.children()[1],
889                         newList = section.children()[2];
890
891                     expect(section.children().length).to.equal(3, 'section has three children');
892                     expect(oldItem1.children()[0].getText()).to.equal('0', 'first item extracted');
893                     expect(oldItem2.children()[0].getText()).to.equal('1', 'second item extracted');
894                     expect(newList.is('list')).to.equal(true, 'list lies below extracted item');
895                     expect(newList.children().length).to.equal(1, 'list has now one child');
896                 });
897
898                 it('puts extracted items below the list if ending item is the last one', function() {
899                     var c = canvas.fromXML('\
900                         <section>\
901                             <div class="list.items">\
902                                 <div class="item">0</div>\
903                                 <div class="item">1</div>\
904                                 <div class="item">2</div>\
905                             </div>\
906                         </section>'),
907                         list = c.doc().children()[0],
908                         item1 = list.children()[0],
909                         item2 = list.children()[1],
910                         item3 = list.children()[2];
911
912                     c.list.extractItems({element1: item2, element2: item3});
913
914                     var section = c.doc(),
915                         oldItem1 = section.children()[1],
916                         oldItem2 = section.children()[2],
917                         newList = section.children()[0];
918
919                     expect(section.children().length).to.equal(3, 'section has three children');
920                     expect(oldItem1.children()[0].getText()).to.equal('1', 'first item extracted');
921                     expect(oldItem2.children()[0].getText()).to.equal('2', 'second item extracted');
922                     expect(newList.is('list')).to.equal(true, 'list lies above extracted item');
923                     expect(newList.children().length).to.equal(1, 'list has now one child');
924                 });
925
926                 it('removes list if all its items are extracted', function() {
927                     var c = canvas.fromXML('\
928                         <section>\
929                             <div class="list.items">\
930                                 <div class="item">some item</div>\
931                                 <div class="item">some item 2</div>\
932                             </div>\
933                         </section>'),
934                         list = c.doc().children()[0],
935                         item1 = list.children()[0],
936                         item2 = list.children()[1];
937
938                     c.list.extractItems({element1: item1, element2: item2});
939
940                     var section = c.doc(),
941                         list1 = section.children()[0],
942                         oldItem1 = section.children()[0],
943                         oldItem2 = section.children()[1];
944
945                     expect(section.children().length).to.equal(2, 'section contains two children');
946                     expect(oldItem1.children()[0].getText()).to.equal('some item');
947                     expect(oldItem2.children()[0].getText()).to.equal('some item 2');
948                 });
949
950                 it('creates two lists with extracted items in the middle if extracting from the middle of the list - nested case' , function() {
951                     var c = canvas.fromXML('\
952                         <section>\
953                             <div class="list.items">\
954                                 <div class="item">0</div>\
955                                 <div class="item">\
956                                     <div class="list.items">\
957                                         <div class="item">1.1</div>\
958                                         <div class="item">1.2</div>\
959                                         <div class="item">1.3</div>\
960                                     </div>\
961                                 </div>\
962                                 <div class="item">2</div>\
963                             </div>\
964                         </section>'),
965                         list = c.doc().children()[0],
966                         nestedList = list.children()[1].children()[0],
967                         nestedListItem = nestedList.children()[1];
968
969                     c.list.extractItems({element1: nestedListItem, element2: nestedListItem});
970
971                     var section = c.doc(),
972                         list = section.children()[0],
973                         item1 = list.children()[0],
974                         item2 = list.children()[1], //
975                         item3 = list.children()[2],
976                         item4 = list.children()[3], //
977                         item5 = list.children()[4],
978                         nestedList1 = item2.children()[0],
979                         nestedList2 = item4.children()[0];
980
981                     expect(list.children().length).to.equal(5, 'top list has five items');
982                     
983                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
984
985                     expect(item2.getWlxmlClass()).to.equal('item', 'first nested list is still wrapped in item element');
986                     expect(nestedList1.children().length).to.equal(1, 'first nested list is left with one child');
987                     expect(nestedList1.children()[0].children()[0].getText()).to.equal('1.1', 'first nested list item left alone');
988                     
989                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
990
991                     expect(item4.getWlxmlClass()).to.equal('item', 'second nested list is still wrapped in item element');
992                     expect(nestedList2.children().length).to.equal(1, 'second nested list is left with one child');
993                     expect(nestedList2.children()[0].children()[0].getText()).to.equal('1.3', 'second nested list item left alone');
994
995                     expect(item5.children()[0].getText()).to.equal('2', 'last item ok');
996                 });
997
998                 it('puts extracted items below the list if ending item is the last one - nested case' , function() {
999                     var c = canvas.fromXML('\
1000                         <section>\
1001                             <div class="list.items">\
1002                                 <div class="item">0</div>\
1003                                 <div class="item">\
1004                                     <div class="list.items">\
1005                                         <div class="item">1.1</div>\
1006                                         <div class="item">1.2</div>\
1007                                         <div class="item">1.3</div>\
1008                                     </div>\
1009                                 </div>\
1010                                 <div class="item">2</div>\
1011                             </div>\
1012                         </section>'),
1013                         list = c.doc().children()[0],
1014                         nestedList = list.children()[1].children()[0],
1015                         nestedListItem1 = nestedList.children()[1],
1016                         nestedListItem2 = nestedList.children()[2];
1017
1018                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
1019
1020                     var section = c.doc(),
1021                         list = section.children()[0],
1022                         item1 = list.children()[0],
1023                         item2 = list.children()[1],
1024                         item3 = list.children()[2],
1025                         item4 = list.children()[3],
1026                         item5 = list.children()[4];
1027                     nestedList = item2.children()[0];
1028
1029                     expect(list.children().length).to.equal(5, 'top list has five items');
1030                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
1031                     expect(item2.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
1032                     expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
1033                     expect(nestedList.children()[0].children()[0].getText()).to.equal('1.1', 'nested list item left alone');
1034                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
1035                     expect(item4.children()[0].getText()).to.equal('1.3', 'fourth item ok');
1036                     expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
1037                 });
1038
1039                 it('puts extracted items above the list if starting item is the first one - nested case' , function() {
1040                     var c = canvas.fromXML('\
1041                         <section>\
1042                             <div class="list.items">\
1043                                 <div class="item">0</div>\
1044                                 <div class="item">\
1045                                     <div class="list.items">\
1046                                         <div class="item">1.1</div>\
1047                                         <div class="item">1.2</div>\
1048                                         <div class="item">1.3</div>\
1049                                     </div>\
1050                                 </div>\
1051                                 <div class="item">2</div>\
1052                             </div>\
1053                         </section>'),
1054                         list = c.doc().children()[0],
1055                         nestedList = list.children()[1].children()[0],
1056                         nestedListItem1 = nestedList.children()[0],
1057                         nestedListItem2 = nestedList.children()[1];
1058
1059                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
1060
1061                     var section = c.doc(),
1062                         list = section.children()[0],
1063                         item1 = list.children()[0],
1064                         item2 = list.children()[1],
1065                         item3 = list.children()[2],
1066                         item4 = list.children()[3],
1067                         item5 = list.children()[4];
1068                     nestedList = item4.children()[0];
1069
1070                     expect(list.children().length).to.equal(5, 'top list has five items');
1071                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
1072                     expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
1073                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
1074                     
1075                     expect(item4.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
1076                     expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
1077                     expect(nestedList.children()[0].children()[0].getText()).to.equal('1.3', 'nested list item left alone');
1078                     expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
1079                 });
1080
1081                 it('removes list if all its items are extracted - nested case', function() {
1082                     var c = canvas.fromXML('\
1083                         <section>\
1084                             <div class="list.items">\
1085                                 <div class="item">0</div>\
1086                                 <div class="item">\
1087                                     <div class="list.items">\
1088                                         <div class="item">1.1</div>\
1089                                         <div class="item">1.2</div>\
1090                                     </div>\
1091                                 </div>\
1092                                 <div class="item">2</div>\
1093                             </div>\
1094                         </section>'),
1095                         list = c.doc().children()[0],
1096                         nestedList = list.children()[1].children()[0],
1097                         nestedListItem1 = nestedList.children()[0],
1098                         nestedListItem2 = nestedList.children()[1];
1099
1100                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
1101
1102                     var section = c.doc(),
1103                         list = section.children()[0],
1104                         item1 = list.children()[0],
1105                         item2 = list.children()[1],
1106                         item3 = list.children()[2],
1107                         item4 = list.children()[3];
1108
1109                     expect(list.children().length).to.equal(4, 'top list has four items');
1110                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
1111                     expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
1112                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
1113                     expect(item4.children()[0].getText()).to.equal('2', 'fourth item ok');
1114                 });
1115
1116                 it('extracts items out of outer most list when merge flag is set to false', function() {
1117                     var c = canvas.fromXML('\
1118                         <section>\
1119                             <div class="list.items">\
1120                                 <div class="item">0</div>\
1121                                 <div class="item">\
1122                                     <div class="list.items">\
1123                                         <div class="item">1.1</div>\
1124                                         <div class="item">1.2</div>\
1125                                     </div>\
1126                                 </div>\
1127                                 <div class="item">2</div>\
1128                             </div>\
1129                         </section>'),
1130                         section = c.doc(),
1131                         list = section.children()[0],
1132                         nestedList = list.children()[1].children()[0],
1133                         nestedListItem = nestedList.children()[0];
1134
1135                     var test = c.list.extractItems({element1: nestedListItem, element2: nestedListItem, merge: false});
1136
1137                     expect(test).to.equal(true, 'extraction status ok');
1138
1139                     var sectionChildren = section.children(),
1140                         extractedItem = sectionChildren[1];
1141
1142                     expect(sectionChildren.length).to.equal(3, 'section has three children');
1143                     expect(sectionChildren[0].is('list')).to.equal(true, 'first child is a list');
1144
1145                     expect(extractedItem.getWlxmlTag()).to.equal('div', 'extracted item is a wlxml div');
1146                     expect(extractedItem.getWlxmlClass()).to.equal(undefined, 'extracted item has no wlxml class');
1147                     expect(extractedItem.children()[0].getText()).to.equal('1.1', 'extracted item ok');
1148                     expect(sectionChildren[2].is('list')).to.equal(true, 'second child is a list');
1149                 });
1150             });
1151         });
1152
1153     });
1154
1155     describe('Cursor', function() {
1156
1157         var getSelection;
1158
1159         var findTextNode = function(inside, text) {
1160             var nodes = inside.find(':not(iframe)').addBack().contents().filter(function() {
1161                 return this.nodeType === Node.TEXT_NODE && this.data === text;
1162             });
1163             if(nodes.length)
1164                 return nodes[0];
1165             return null;
1166         }
1167
1168         beforeEach(function() {
1169             getSelection = sinon.stub(window, 'getSelection');
1170         });
1171
1172         afterEach(function() {
1173             getSelection.restore();
1174         });
1175
1176         it('returns position when browser selection collapsed', function() {
1177             var c = canvas.fromXML('<section>Alice has a cat</section>'),
1178                 dom = c.doc().dom(),
1179                 text = findTextNode(dom, 'Alice has a cat');
1180
1181             expect(text.nodeType).to.equal(Node.TEXT_NODE, 'correct node selected');
1182             expect($(text).text()).to.equal('Alice has a cat');
1183
1184             getSelection.returns({
1185                 anchorNode: text,
1186                 focusNode: text,
1187                 anchorOffset: 5,
1188                 focusOffset: 5,
1189                 isCollapsed: true
1190             });
1191             var cursor = c.getCursor(),
1192                 position = cursor.getPosition();
1193
1194             expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything');
1195             expect(position.element.getText()).to.equal('Alice has a cat');
1196             expect(position.offset).to.equal(5);
1197             expect(position.offsetAtEnd).to.equal(false, 'offset is not at end');
1198
1199             getSelection.returns({
1200                 anchorNode: text,
1201                 focusNode: text,
1202                 anchorOffset: 15,
1203                 focusOffset: 15,
1204                 isCollapsed: true
1205             });
1206
1207             expect(cursor.getPosition().offsetAtEnd).to.equal(true, 'offset at end');
1208         });
1209
1210         it('returns boundries of selection when browser selection not collapsed', function() {
1211             var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
1212                 dom = c.doc().dom(),
1213                 text = {
1214                     alice: findTextNode(dom, 'Alice '),
1215                     has: findTextNode(dom, 'has'),
1216                     cat: findTextNode(dom, ' cat')
1217                 },
1218                 cursor = c.getCursor(),
1219                 aliceElement = c.getDocumentElement(text.alice),
1220                 catElement = c.getDocumentElement(text.cat);
1221
1222
1223                 [
1224                     {focus: text.alice, focusOffset: 1, anchor: text.cat,   anchorOffset: 2, selectionAnchor: catElement},
1225                     {focus: text.cat,   focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement}
1226                 ].forEach(function(s, idx) {
1227                     getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset});
1228
1229                     var selectionStart = cursor.getSelectionStart(),
1230                         selectionEnd = cursor.getSelectionEnd(),
1231                         selectionAnchor = cursor.getSelectionAnchor();
1232
1233                     expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting');
1234                     expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx);
1235                     expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx);
1236                     expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx);
1237                     expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx);
1238                     expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok');
1239                     expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok');
1240                 });
1241         });
1242
1243         it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() {
1244             var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
1245                 dom = c.doc().dom(),
1246                 text = {
1247                     alice: findTextNode(dom, 'Alice '),
1248                     has: findTextNode(dom, 'has'),
1249                     a: findTextNode(dom, ' a '),
1250                     big: findTextNode(dom, 'big'),
1251                     cat: findTextNode(dom, ' cat'),
1252                 },
1253                 cursor = c.getCursor();
1254
1255             expect($(text.alice).text()).to.equal('Alice ');
1256             expect($(text.has).text()).to.equal('has');
1257             expect($(text.a).text()).to.equal(' a ');
1258             expect($(text.big).text()).to.equal('big');
1259             expect($(text.cat).text()).to.equal(' cat');
1260
1261             getSelection.returns({anchorNode: text.alice, focusNode: text.a});
1262             expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children');
1263
1264             getSelection.returns({anchorNode: text.alice, focusNode: text.cat});
1265             expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children');
1266
1267             getSelection.returns({anchorNode: text.alice, focusNode: text.has});
1268             expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children');
1269
1270             getSelection.returns({anchorNode: text.has, focusNode: text.big});
1271             expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children');
1272             
1273         })
1274
1275         describe('zero width space handling', function() {
1276             it('position range includes ZWS at the boundries of text in case when native selection api doesn\'t', function() {
1277                 var c = canvas.fromXML("<section>Alice</section>"),
1278                     dom = c.doc().dom(),
1279                     textNode = findTextNode(dom, 'Alice'),
1280                     cursor = c.getCursor();
1281
1282                 textNode.data = utils.unicode.ZWS + 'Alice';
1283                 getSelection.returns({anchorNode: textNode, anchorOffset: 1, focusNode: textNode, focusOffset: 1});
1284                 expect(cursor.getPosition().offset).to.equal(0);
1285                 expect(cursor.getPosition().offsetAtBeginning).to.equal(true, 'offset at beginning');
1286                 
1287                 textNode.data = 'Alice' + utils.unicode.ZWS;
1288                 getSelection.returns({anchorNode: textNode, anchorOffset: 5, focusNode: textNode, focusOffset: 5});
1289                 expect(cursor.getPosition().offset).to.equal(6);
1290                 expect(cursor.getPosition().offsetAtEnd).to.equal(true, 'offset at end');
1291             });
1292         });
1293     });
1294
1295     describe('Serializing document to WLXML', function() {
1296         it('keeps document intact when no changes have been made', function() {
1297             var xmlIn = '<section>Alice<div>has</div>a <span class="uri" meta-uri="http://cat.com">cat</span>!</section>',
1298                 c = canvas.fromXML(xmlIn),
1299                 xmlOut = c.toXML();
1300
1301             var parser = new DOMParser(),
1302                 input = parser.parseFromString(xmlIn, "application/xml").childNodes[0],
1303                 output = parser.parseFromString(xmlOut, "application/xml").childNodes[0];
1304             
1305             expect(input.isEqualNode(output)).to.be.true;
1306         });
1307
1308         it('keeps arbitrary node attributes intact', function() {
1309             var xmlIn = '<section a="1" xmlns:dcterms="http://purl.org/dc/terms/"></section>',
1310                 $xmlOut = $(canvas.fromXML(xmlIn).toXML());
1311
1312             expect($xmlOut.attr('a')).to.equal('1');
1313             expect($xmlOut.attr('xmlns:dcterms')).to.equal('http://purl.org/dc/terms/');
1314         });
1315
1316         it('doesn\' serialize meta attribute if its empty', function() {
1317             var c;
1318
1319             c = canvas.fromXML('<section class="uri" meta-uri="some.uri"></section>');
1320             c.doc().setWlxmlMetaAttr('uri', '');
1321             expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'overriding attribute with zero length string');
1322
1323             c = canvas.fromXML('<section class="uri"></section>');
1324             c.doc().setWlxmlMetaAttr('uri', '');
1325             expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'setting attribute to zero length string');
1326         });
1327
1328         describe('output xml', function() {
1329             it('keeps entities intact', function() {
1330                 var xmlIn = '<section>&lt; &gt;</section>',
1331                     c = canvas.fromXML(xmlIn),
1332                     xmlOut = c.toXML();
1333                 expect(xmlOut).to.equal(xmlIn);
1334             });
1335             it('keeps entities intact when they form html/xml', function() {
1336                 var xmlIn = '<section>&lt;abc&gt;</section>',
1337                     c = canvas.fromXML(xmlIn),
1338                     xmlOut = c.toXML();
1339                 expect(xmlOut).to.equal(xmlIn);
1340             });
1341         });
1342
1343         describe('formatting output xml', function() {
1344             /*it('keeps white spaces at the edges of input xml', function() {
1345                 var xmlIn = '  <section></section>  ',
1346                 c = canvas.fromXML(xmlIn),
1347                 xmlOut = c.toXML();
1348
1349                 expect(xmlOut.substr(4)).to.equal('   <', 'start');
1350                 expect(xmlOut.substr(-2)).to.equal('>  ', 'end');
1351             });*/
1352             it('keeps white space between XML nodes', function() {
1353                 var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
1354                 c = canvas.fromXML(xmlIn),
1355                 xmlOut = c.toXML();
1356
1357                 var partsIn = xmlIn.split('\n\n\n'),
1358                     partsOut = xmlOut.split('\n\n\n');
1359                 
1360                 expect(partsIn).to.deep.equal(partsOut);
1361             });
1362
1363             it('keeps white space between XML nodes - inline case', function() {
1364                 var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
1365                 c = canvas.fromXML(xmlIn);
1366                 
1367                 var xmlOut = c.toXML();
1368
1369                 var partsIn = xmlIn.split('\n\n\n'),
1370                     partsOut = xmlOut.split('\n\n\n');
1371                 
1372                 expect(partsIn).to.deep.equal(partsOut);
1373             });
1374
1375             it('keeps white space at the beginning of text', function() {
1376                 var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
1377                     c = canvas.fromXML(xmlIn),
1378                     xmlOut = c.toXML();
1379
1380                 expect(xmlOut).to.equal(xmlIn);
1381             });
1382
1383             it('nests new children block elements', function() {
1384                 var c = canvas.fromXML('<section></section>');
1385     
1386                 c.doc().append({tag: 'header'});
1387
1388                 var xmlOut = c.toXML();
1389                 expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
1390                 expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
1391
1392             });
1393
1394             it('doesn\'t nest new children inline elements', function() {
1395                 var c = canvas.fromXML('<section></section>');
1396     
1397                 c.doc().append({tag: 'span'});
1398
1399                 var xmlOut = c.toXML();
1400                 expect(xmlOut).to.equal('<section><span></span></section>');
1401             });
1402
1403             it('keeps original white space at the end of text', function() {
1404                 
1405                 var xmlIn = '<header>    Some text ended with white space \
1406                 \
1407                 <span class="uri">Some text</span> some text\
1408             \
1409             </header>',
1410                     c = canvas.fromXML(xmlIn);
1411
1412             var xmlOut = c.toXML();
1413             console.log(xmlOut);
1414             expect(xmlOut).to.equal(xmlIn);
1415             });
1416
1417             it('keeps white space around text node', function() {
1418                 var xmlIn = '<section>\
1419                 <header>header1</header>\
1420                 Some text surrounded by white space\
1421                 <header>header2</header>\
1422             </section>',
1423                     c = canvas.fromXML(xmlIn);
1424
1425                 var xmlOut = c.toXML();
1426                 expect(xmlOut).to.equal(xmlIn);
1427             });
1428
1429             it('keeps white space around text node - last node case', function() {
1430                 var xmlIn = '<section>\
1431                 <header>header</header>\
1432                     \
1433                 Some text surrounded by white space\
1434                     \
1435             </section>',
1436                     c = canvas.fromXML(xmlIn);
1437
1438                 var xmlOut = c.toXML();
1439                 expect(xmlOut).to.equal(xmlIn);
1440             });
1441
1442             it('keeps white space after detaching text element', function() {
1443                 var xmlIn = '<section><header>header</header>\n\
1444                     \n\
1445                 text1\n\
1446                     \n\
1447             </section>',
1448                     expectedXmlOut = '<section><header>header</header>\n\
1449                     \n\
1450                 \n\
1451                     \n\
1452             </section>',
1453                     c = canvas.fromXML(xmlIn),
1454                     children = c.doc().children(),
1455                     text = children[children.length-1];
1456                 
1457                 expect(text.getText()).to.equal('text1');
1458
1459                 text.detach();
1460
1461                 var xmlOut = c.toXML();
1462                 expect(xmlOut).to.equal(expectedXmlOut);
1463             });
1464
1465         })
1466     })
1467 });
1468
1469
1470 });