Root DocumentElement has no parent
[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 ], function(chai, sinon, canvas, documentElement) {
7     
8 'use strict';
9
10 var expect = chai.expect;
11
12
13 describe('Canvas', function() {
14
15     describe('Internal HTML representation of a sample document', function() {
16         it('works', function() {
17             var c = canvas.fromXML('\
18                 <section>\
19                     This is some text without its own wrapping tag.\
20                     <div class="p.subclass">\
21                         This is a paragraph.\
22                     </div>\
23                     <div>\
24                         This is text in a div <span>with some inline text</span>.\
25                     </div>\
26                     This is some text without its own wrapping tag.\
27                 </section>\
28             ');
29             var expected = '<div wlxml-tag="section">'
30                             + 'This is some text without its own wrapping tag.'
31                             + '<div wlxml-tag="div" wlxml-class="p-subclass">This is a paragraph.</div>'
32                             + '<div wlxml-tag="div">This is text in a div <div wlxml-tag="span">with some inline text</div>.</div>'
33                             + 'This is some text without its own wrapping tag.'
34                             + '</div>';
35             expect(c.doc().dom()[0].isEqualNode($(expected)[0])).to.be.true;
36         });
37     });
38
39     describe('Internal HTML representation of a DocumentNodeElement', function() {
40         it('is always a div tag', function() {
41             ['section', 'header', 'span', 'aside', 'figure'].forEach(function(tagName) {
42                 var dom = canvas.fromXML('<' + tagName + '></' + tagName + '>').doc().dom();
43                 expect(dom.prop('tagName')).to.equal('DIV', tagName + ' is represented as div');
44             });
45         });
46         it('has wlxml tag put into wlxml-tag attribute', function() {
47             var dom = canvas.fromXML('<section></section>').doc().dom();
48             expect(dom.attr('wlxml-tag')).to.equal('section');
49         });
50         it('has wlxml class put into wlxml-class, dots replaced with dashes', function() {
51             var dom = canvas.fromXML('<section class="some.class"></section>').doc().dom();
52             expect(dom.attr('wlxml-class')).to.equal('some-class');
53         });
54     });
55
56     describe('Internal HTML representation of a DocumentTextElement', function() {
57         it('is just a TextNode', function() {
58             var dom = canvas.fromXML('<section>Alice</section>').doc().children()[0].dom();
59             expect(dom[0].nodeType === Node.TEXT_NODE);
60         });
61     });
62
63     describe('basic properties', function() {
64         it('renders empty document when canvas created from empty XML', function() {
65             var c = canvas.fromXML('');
66             expect(c.doc()).to.equal(null);
67         });
68
69         it('gives access to its document root node', function() {
70             var c = canvas.fromXML('<section></section>');
71             expect(c.doc().getWlxmlTag()).to.equal('section');
72         });
73
74         describe('root element', function() {
75             it('has no parent', function() {
76                 var c = canvas.fromXML('<section></section>');
77                 expect(c.doc().parent()).to.be.null;
78             });
79         });
80
81         describe('DocumentTextElement', function() {
82             it('can have its content set', function() {
83                 var c = canvas.fromXML('<section>Alice</section>'),
84                     root = c.doc(),
85                     text = root.children()[0];
86                 
87                 text.setText('a cat');
88                 expect(root.children()[0].getText()).to.equal('a cat');
89             });
90         });
91
92         describe('DocumentNodeElement', function() {
93             it('knows index of its child', function() {
94                 var c = canvas.fromXML('<section><div></div><header></header><span></span></section>'),
95                     root = c.doc(),
96                     child = root.children()[1];
97                 expect(root.childIndex(child)).to.equal(1);
98             });
99
100             it('knows WLXML tag it renders', function(){
101                 var c = canvas.fromXML('<section></section>'),
102                     section = c.doc();
103                 expect(section.getWlxmlTag()).to.equal('section', 'initial tag is section');
104                 section.setWlxmlTag('header');
105                 expect(section.getWlxmlTag()).to.equal('header', 'tag is changed to header');
106             });
107
108             it('knows WLXML class of a WLXML tag it renders', function(){
109                 var c = canvas.fromXML('<section class="some.class"></section>'),
110                     section = c.doc();
111                 expect(section.getWlxmlClass()).to.equal('some.class');
112                 section.setWlxmlClass('some.other.class');
113                 expect(section.getWlxmlClass()).to.equal('some.other.class');
114                 section.setWlxmlClass(null);
115                 expect(section.getWlxmlClass()).to.be.undefined;
116             });
117         });
118
119         it('returns DocumentNodeElement instance from HTMLElement', function() {
120             var c = canvas.fromXML('<section></section>'),
121                 htmlElement = c.doc().dom().get(0),
122                 element = c.getDocumentElement(htmlElement);
123             expect(element).to.be.instanceOf(documentElement.DocumentNodeElement);
124             expect(element.sameNode(c.doc()));
125         });
126         
127         it('returns DocumentTextElement instance from Text Node', function() {
128             var c = canvas.fromXML('<section>Alice</section>'),
129                 aliceElement = c.doc().children()[0],
130                 textNode = aliceElement.dom()[0],
131                 element = c.getDocumentElement(textNode);
132
133             expect(textNode.nodeType).to.equal(Node.TEXT_NODE, 'text node selected');
134             expect($(textNode).text()).to.equal('Alice');
135
136             expect(element).to.be.instanceOf(documentElement.DocumentTextElement);
137             expect(element.sameNode(c.doc().children()[0]));
138         });
139     });
140
141
142
143     describe('document representation api', function() {
144         describe('document root element', function() {
145             var c = canvas.fromXML('<section></section>');
146             it('exists', function() {
147                 expect(c.doc()).to.be.instanceOf(documentElement.DocumentElement);
148             });
149             it('is of type DocumentNodeElement', function() {
150                 expect(c.doc()).to.be.instanceOf(documentElement.DocumentNodeElement);
151             });
152         });
153
154         describe('DocumentElements comparison', function() {
155             it('reports dwo DocumentElements to be the same when they represent the same wlxml document element', function() {
156                 var c = canvas.fromXML('<section><div></div><div></div></section>'),
157                     first_div1 = c.doc().children()[0],
158                     first_div2 = c.doc().children()[0],
159                     second_div = c.doc().children()[1];
160                 expect(first_div1.sameNode(first_div1)).to.be.true;
161                 expect(first_div1.sameNode(first_div2)).to.be.true;
162                 expect(first_div1.sameNode(second_div)).to.be.false;
163             });
164         });
165
166         describe('traversing', function() {
167             it('reports element nodes', function() {
168                 var c = canvas.fromXML('<section><div></div></section>'),
169                     children = c.doc().children();
170                 expect(children.length).to.equal(1);
171                 expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
172
173                 c = canvas.fromXML('<section><div></div><div></div></section>'),
174                     children = c.doc().children();
175                 expect(children.length).to.equal(2);
176                 expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
177                 expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
178             });
179             
180             it('reports text nodes', function() {
181                 var c = canvas.fromXML('<section>Alice</section>'),
182                     children = c.doc().children();
183                 expect(children.length).to.equal(1);
184                 expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
185             });
186
187             describe('accessing parents', function() {
188                 it('returns DocumentNodeElement representing parent in wlxml document as DocumentNodeElement parent', function() {
189                     var c = canvas.fromXML('<section><div></div></section>'),
190                         div = c.doc().children()[0];
191                     expect(div.parent().sameNode(c.doc())).to.be.true;
192                 });
193                 it('returns DocumentNodeElement representing parent in wlxml document as DocumentTextElement parent', function() {
194                     var c = canvas.fromXML('<section>Alice</section>'),
195                         text = c.doc().children()[0];
196                     expect(text.parent().sameNode(c.doc())).to.be.true;
197                 });
198             });
199
200             describe('free text handling', function() {
201                     it('sees free text', function() {
202                         var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
203                             children = c.doc().children();
204                         expect(children.length).to.equal(3);
205                         expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
206                         expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
207                         expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
208                     });
209             });
210             
211             describe('white characters handling', function() {
212                 it('says empty element node has no children', function() {
213                     var c = canvas.fromXML('<section></section>');
214                     expect(c.doc().children().length).to.equal(0);
215                 });
216                 it('says element node with one space has one DocumentTextElement', function() {
217                     var c = canvas.fromXML('<section> </section>');
218                     expect(c.doc().children().length).to.equal(1);
219                     expect(c.doc().children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
220                     expect(c.doc().children()[0].getText()).to.equal(' ');
221                 });
222                 it('ignores white space surrounding block elements', function() {
223                     var c = canvas.fromXML('<section> <div></div> </section>');
224                     var children = c.doc().children();
225                     expect(children.length).to.equal(1);
226                     expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
227                 });
228                 it('ignores white space between block elements', function() {
229                     var c = canvas.fromXML('<section><div></div> <div></div></section>');
230                     var children = c.doc().children();
231                     expect(children.length === 2);
232                     [0,1].forEach(function(idx) {
233                         expect(children[idx]).to.be.instanceOf(documentElement.DocumentNodeElement);
234                     });
235                 });
236
237                 it('trims white space from the beginning and the end of the block elements', function() {
238                     var c = canvas.fromXML('<section> Alice <span>has</span> a cat </section>');
239                     expect(c.doc().children()[0].getText()).to.equal('Alice ');
240                     expect(c.doc().children()[2].getText()).to.equal(' a cat');
241                 });
242
243                 it('normalizes string of white characters to one space at the inline element boundries', function() {
244                     var c = canvas.fromXML('<span>   Alice has a cat   </span>');
245                     expect(c.doc().children()[0].getText()).to.equal(' Alice has a cat ');
246                 });
247
248                 it('normalizes string of white characters to one space before inline element', function() {
249                     var c = canvas.fromXML('<div>Alice has  <span>a cat</span></div>');
250                     expect(c.doc().children()[0].getText()).to.equal('Alice has ');
251                 });
252                 
253                 it('normalizes string of white characters to one space after inline element', function() {
254                     var c = canvas.fromXML('<div>Alice has <span>a</span>  cat</div>');
255                     expect(c.doc().children()[2].getText()).to.equal(' cat');
256                 });
257             });
258         });
259
260         describe('manipulation api', function() {
261
262             describe('Basic Element inserting', function() {
263                 it('can put new NodeElement at the end', function() {
264                     var c = canvas.fromXML('<section></section>'),
265                         appended = c.doc().append({tag: 'header', klass: 'some.class'}),
266                         children = c.doc().children();
267
268                     expect(children.length).to.equal(1);
269                     expect(children[0].sameNode(appended));
270                 });
271
272                 it('can put new TextElement at the end', function() {
273                     var c = canvas.fromXML('<section></section>'),
274                         appended = c.doc().append({text: 'Alice'}),
275                         children = c.doc().children();
276
277                     expect(children.length).to.equal(1);
278                     expect(children[0].sameNode(appended));
279                     expect(children[0].getText()).to.equal('Alice');
280                 });
281
282                 it('can put new NodeElement after another NodeElement', function() {
283                     var c = canvas.fromXML('<section><div></div></section>'),
284                         div = c.doc().children()[0],
285                         added = div.after({tag: 'header', klass: 'some.class'}),
286                         children = c.doc().children();
287                     expect(children.length).to.equal(2);
288                     expect(children[1].sameNode(added));
289                 });
290
291                 it('can put new Nodeelement before another element', function() {
292                     var c = canvas.fromXML('<section><div></div></section>'),
293                         div = c.doc().children()[0],
294                         added = div.before({tag: 'header', klass: 'some.class'}),
295                         children = c.doc().children();
296                     expect(children.length).to.equal(2);
297                     expect(children[0].sameNode(added));
298                 });
299
300                 it('can put new DocumentNodeElement after DocumentTextElement', function() {
301                     var c = canvas.fromXML('<section>Alice</section>'),
302                         text = c.doc().children()[0],
303                         added = text.after({tag: 'p'}),
304                         children = c.doc().children();
305
306                     expect(children.length).to.equal(2);
307                     expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
308                     expect(children[0].getText()).to.equal('Alice');
309                     expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
310                     expect(children[1].sameNode(added)).to.be.true;
311                 });
312                 it('can put new DocumentNodeElement before DocumentTextElement', function() {
313                     var c = canvas.fromXML('<section>Alice</section>'),
314                         text = c.doc().children()[0],
315                         added = text.before({tag: 'p'}),
316                         children = c.doc().children();
317
318                     expect(children.length).to.equal(2);
319                     expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
320                     expect(children[0].sameNode(added)).to.be.true;
321                     expect(children[1]).to.be.instanceOf(documentElement.DocumentTextElement);
322                     expect(children[1].getText()).to.equal('Alice');
323                 });
324             });
325
326             describe('Splitting text', function() {
327                 
328                 it('splits DocumentTextElement\'s parent into two DocumentNodeElements of the same type', function() {
329                     var c = canvas.fromXML('<section><header>Some header</header></section>'),
330                         section = c.doc(),
331                         text = section.children()[0].children()[0];
332
333                     text.split({offset: 5});
334                     expect(section.children().length).to.equal(2, 'section has two children');
335                     
336                     var header1 = section.children()[0];
337                     var header2 = section.children()[1];
338
339                     expect(header1.getWlxmlTag()).to.equal('header', 'first section child represents wlxml header');
340                     expect(header1.children().length).to.equal(1, 'first header has one text child');
341                     expect(header1.children()[0].getText()).to.equal('Some ', 'first header has correct content');
342                     expect(header2.getWlxmlTag()).to.equal('header', 'second section child represents wlxml header');
343                     expect(header2.children().length).to.equal(1, 'second header has one text child');
344                     expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content');
345                 });
346
347                 it('leaves empty copy of DocumentNodeElement if splitting at the very beginning', function() {
348                         var c = canvas.fromXML('<section><header>Some header</header></section>'),
349                         section = c.doc(),
350                         text = section.children()[0].children()[0];
351
352                         text.split({offset: 0});
353                         
354                         var header1 = section.children()[0];
355                         var header2 = section.children()[1];
356
357                         expect(header1.children().length).to.equal(0);
358                         expect(header2.children()[0].getText()).to.equal('Some header');
359                 });
360
361                 it('leaves empty copy of DocumentNodeElement if splitting at the very end', function() {
362                         var c = canvas.fromXML('<section><header>Some header</header></section>'),
363                         section = c.doc(),
364                         text = section.children()[0].children()[0];
365
366                         text.split({offset: 11});
367                         
368                         var header1 = section.children()[0];
369                         var header2 = section.children()[1];
370
371                         expect(header1.children()[0].getText()).to.equal('Some header');
372                         expect(header2.children().length).to.equal(0);
373                 });
374
375                 it('keeps DocumentTextElement\'s parent\'s children elements intact', function() {
376                     var c = canvas.fromXML('\
377                             <section>\
378                                 <header>\
379                                     A <span>fancy</span> and <span>nice</span> header\
380                                 </header>\
381                             </section>'),
382                         section = c.doc(),
383                         header = section.children()[0],
384                         textAnd = header.children()[2];
385
386                     textAnd.split({offset: 2});
387                     
388                     var sectionChildren = section.children();
389                     expect(sectionChildren.length).to.equal(2, 'Section has two children');
390                     expect(sectionChildren[0].getWlxmlTag()).to.equal('header', 'First section element is a wlxml header');
391                     expect(sectionChildren[1].getWlxmlTag()).to.equal('header', 'Second section element is a wlxml header');
392
393                     var firstHeaderChildren = sectionChildren[0].children();
394                     expect(firstHeaderChildren.length).to.equal(3, 'First header has three children');
395                     expect(firstHeaderChildren[0].getText()).to.equal('A ', 'First header starts with a text');
396                     expect(firstHeaderChildren[1].getWlxmlTag()).to.equal('span', 'First header has span in the middle');
397                     expect(firstHeaderChildren[2].getText()).to.equal(' a', 'First header ends with text');
398
399                     var secondHeaderChildren = sectionChildren[1].children();
400                     expect(secondHeaderChildren.length).to.equal(3, 'Second header has three children');
401                     expect(secondHeaderChildren[0].getText()).to.equal('nd ', 'Second header starts with text');
402                     expect(secondHeaderChildren[1].getWlxmlTag()).to.equal('span', 'Second header has span in the middle');
403                     expect(secondHeaderChildren[2].getText()).to.equal(' header', 'Second header ends with text');
404                 });
405             });
406
407             describe('wrapping', function() {
408                 it('wraps DocumentNodeElement', function() {
409                     var c = canvas.fromXML('<section><div></div></section>'),
410                         div = c.doc().children()[0];
411                     
412                     var returned = div.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
413                         parent = div.parent(),
414                         parent2 = c.doc().children()[0];
415
416                     expect(returned.sameNode(parent)).to.be.true;
417                     expect(returned.sameNode(parent2)).to.be.true;
418                     expect(returned.getWlxmlTag()).to.equal('header');
419                     expect(returned.getWlxmlClass()).to.equal('some.class');
420                 });
421                 it('wraps DocumentTextElement', function() {
422                     var c = canvas.fromXML('<section>Alice</section>'),
423                         text = c.doc().children()[0];
424                     
425                     var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
426                         parent = text.parent(),
427                         parent2 = c.doc().children()[0];
428
429                     expect(returned.sameNode(parent)).to.be.true;
430                     expect(returned.sameNode(parent2)).to.be.true;
431                     expect(returned.getWlxmlTag()).to.equal('header');
432                     expect(returned.getWlxmlClass()).to.equal('some.class');
433                 });
434                 
435                 describe('wrapping part of DocumentTextElement', function() {
436                     [{start: 5, end: 12}, {start: 12, end: 5}].forEach(function(offsets) {
437                         it('wraps in the middle ' + offsets.start + '/' + offsets.end, function() {
438                             var c = canvas.fromXML('<section>Alice has a cat</section>'),
439                                 text = c.doc().children()[0];
440                             
441                             var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: offsets.start, end: offsets.end}),
442                                 children = c.doc().children();
443
444                             expect(children.length).to.equal(3);
445                             
446                             expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
447                             expect(children[0].getText()).to.equal('Alice');
448
449                             expect(children[1].sameNode(returned)).to.be.true;
450                             expect(returned.getWlxmlTag()).to.equal('header');
451                             expect(returned.getWlxmlClass()).to.equal('some.class');
452                             expect(children[1].children().length).to.equal(1);
453                             expect(children[1].children()[0].getText()).to.equal(' has a ');
454
455                             expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
456                             expect(children[2].getText()).to.equal('cat');
457                         });
458                     });
459
460                     it('wraps whole text inside DocumentTextElement if offsets span entire content', function() {
461                          var c = canvas.fromXML('<section>Alice has a cat</section>'),
462                              text = c.doc().children()[0];
463                          
464                          var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 0, end: 15}),
465                              children = c.doc().children();
466
467                          expect(children.length).to.equal(1);
468                          expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
469                          expect(children[0].children()[0].getText()).to.equal('Alice has a cat');
470                     });
471                 });
472
473                 it('wraps text spanning multiple sibling DocumentTextNodes', function() {
474                     var c = canvas.fromXML('<section>Alice has a <span>small</span> cat</section>'),
475                         section = c.doc(),
476                         wrapper = c.wrapText({
477                             inside: section, 
478                             _with: {tag: 'span', klass: 'some.class'},
479                             offsetStart: 6,
480                             offsetEnd: 4,
481                             textNodeIdx: [0,2]
482                         });
483
484                     expect(section.children().length).to.equal(2);
485                     expect(section.children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
486                     expect(section.children()[0].getText()).to.equal('Alice ');
487
488                     var wrapper2 = section.children()[1];
489                     expect(wrapper2.sameNode(wrapper)).to.be.true;
490
491                     var wrapperChildren = wrapper.children();
492                     expect(wrapperChildren.length).to.equal(3);
493                     expect(wrapperChildren[0].getText()).to.equal('has a ');
494
495                     expect(wrapperChildren[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
496                     expect(wrapperChildren[1].children().length).to.equal(1);
497                     expect(wrapperChildren[1].children()[0].getText()).to.equal('small');
498
499                     expect(wrapperChildren[2].getText()).to.equal(' cat');
500                 });
501             });
502
503             describe('unwrapping', function() {
504                 it('unwraps DocumentTextElement from its parent DocumentNodeElement if it\'s its only child', function() {
505                     var c = canvas.fromXML('<section><div>Alice has a cat</div></section>'),
506                     section = c.doc(),
507                     text = section.children()[0].children()[0];
508
509                     text.unwrap();
510
511                     expect(section.children().length).to.equal(1);
512                     expect(section.children()[0].getText()).to.equal('Alice has a cat');
513                 })
514             });
515         });
516
517         describe('Lists api', function() {
518             describe('creating lists', function() {
519                 it('allows creation of a list from existing sibling DocumentElements', function() {
520                     var c = canvas.fromXML('\
521                         <section>\
522                             Alice\
523                             <div>has</div>\
524                             a\
525                             <div>cat</div>\
526                         </section>'),
527                         section = c.doc(),
528                         textHas = section.children()[1],
529                         divA = section.children()[2]
530                     
531                     c.list.create({element1: textHas, element2: divA});
532
533                     expect(section.children().length).to.equal(3, 'section has three child elements');
534
535                     var child1 = section.children()[0],
536                         list = section.children()[1],
537                         child3 = section.children()[2];
538
539                     expect(child1.getText()).to.equal('Alice');
540                     expect(list.is('list')).to.equal(true, 'second child is a list');
541                     expect(list.children().length).to.equal(2, 'list contains two elements');
542                     list.children().forEach(function(child) {
543                         expect(child.getWlxmlClass()).to.equal('item', 'list childs have wlxml class of item');
544                     });
545                     expect(child3.children()[0].getText()).to.equal('cat');
546                 });
547                 
548                 it('allows creating nested list from existing sibling list items', function() {
549                     var c = canvas.fromXML('\
550                         <section>\
551                             <div class="list-items">\
552                                 <div class="item">A</div>\
553                                 <div class="item">B</div>\
554                                 <div class="item">C</div>\
555                                 <div class="item">D</div>\
556                             </div>\
557                         </section>'),
558                         outerList = c.doc().children()[0],
559                         itemB = outerList.children()[1],
560                         itemC = outerList.children()[2];
561
562
563                         c.list.create({element1: itemB, element2: itemC});
564
565                     var outerListItems = outerList.children(),
566                         innerList = outerListItems[1].children()[0],
567                         innerListItems = innerList.children();
568
569                     expect(outerListItems.length).to.equal(3, 'outer list has three items');
570                     expect(outerListItems[0].children()[0].getText()).to.equal('A', 'first outer item ok');
571                     expect(outerListItems[1].getWlxmlClass()).to.equal('item', 'inner list is wrapped by item element');
572
573                     expect(innerList.is('list')).to.equal(true, 'inner list created');
574                     expect(innerListItems.length).to.equal(2, 'inner list has two items');
575                     expect(innerListItems[0].children()[0].getText()).to.equal('B', 'first inner item ok');
576                     expect(innerListItems[1].children()[0].getText()).to.equal('C', 'second inner item ok');
577
578                     expect(outerListItems[2].children()[0].getText()).to.equal('D', 'last outer item ok');
579
580                 });
581
582             });
583
584             describe('extracting list items', function() {
585                 it('creates two lists with extracted items in the middle if extracting from the middle of the list', function() {
586                     var c = canvas.fromXML('\
587                         <section>\
588                             <div class="list.items">\
589                                 <div class="item">0</div>\
590                                 <div class="item">1</div>\
591                                 <div class="item">2</div>\
592                                 <div class="item">3</div>\
593                             </div>\
594                         </section>'),
595                         list = c.doc().children()[0],
596                         item1 = list.children()[1],
597                         item2 = list.children()[2];
598
599                     c.list.extractItems({element1: item1, element2: item2});
600
601                     var section = c.doc(),
602                         list1 = section.children()[0],
603                         oldItem1 = section.children()[1],
604                         oldItem2 = section.children()[2],
605                         list2 = section.children()[3];
606
607                     expect(section.children().length).to.equal(4, 'section contains four children');
608                     
609                     expect(list1.is('list')).to.equal(true, 'first section child is a list');
610                     expect(list1.children().length).to.equal(1, 'first list has one child');
611                     expect(list1.children()[0].children()[0].getText()).to.equal('0', 'first item of the first list is a first item of the original list');
612
613                     expect(oldItem1.children()[0].getText()).to.equal('1', 'first item got extracted');
614                     expect(oldItem1.getWlxmlClass() === undefined).to.equal(true, 'first extracted element has no wlxml class');
615
616                     expect(oldItem2.children()[0].getText()).to.equal('2', 'second item got extracted');
617                     expect(oldItem2.getWlxmlClass() === undefined).to.equal(true, 'second extracted element has no wlxml class');
618
619                     expect(list2.is('list')).to.equal(true, 'last section child is a list');
620                     expect(list2.children().length).to.equal(1, 'second list has one child');
621                     expect(list2.children()[0].children()[0].getText()).to.equal('3', 'first item of the second list is a last item of the original list');
622                 });
623
624                 it('puts extracted items above the list if starting item is the first one', function() {
625                     var c = canvas.fromXML('\
626                         <section>\
627                             <div class="list.items">\
628                                 <div class="item">0</div>\
629                                 <div class="item">1</div>\
630                                 <div class="item">2</div>\
631                             </div>\
632                         </section>'),
633                         list = c.doc().children()[0],
634                         item1 = list.children()[0],
635                         item2 = list.children()[1],
636                         item3 = list.children()[2];
637
638                     c.list.extractItems({element1: item1, element2: item2});
639
640                     var section = c.doc(),
641                         oldItem1 = section.children()[0],
642                         oldItem2 = section.children()[1],
643                         newList = section.children()[2];
644
645                     expect(section.children().length).to.equal(3, 'section has three children');
646                     expect(oldItem1.children()[0].getText()).to.equal('0', 'first item extracted');
647                     expect(oldItem2.children()[0].getText()).to.equal('1', 'second item extracted');
648                     expect(newList.is('list')).to.equal(true, 'list lies below extracted item');
649                     expect(newList.children().length).to.equal(1, 'list has now one child');
650                 });
651
652                 it('puts extracted items below the list if ending item is the last one', function() {
653                     var c = canvas.fromXML('\
654                         <section>\
655                             <div class="list.items">\
656                                 <div class="item">0</div>\
657                                 <div class="item">1</div>\
658                                 <div class="item">2</div>\
659                             </div>\
660                         </section>'),
661                         list = c.doc().children()[0],
662                         item1 = list.children()[0],
663                         item2 = list.children()[1],
664                         item3 = list.children()[2];
665
666                     c.list.extractItems({element1: item2, element2: item3});
667
668                     var section = c.doc(),
669                         oldItem1 = section.children()[1],
670                         oldItem2 = section.children()[2],
671                         newList = section.children()[0];
672
673                     expect(section.children().length).to.equal(3, 'section has three children');
674                     expect(oldItem1.children()[0].getText()).to.equal('1', 'first item extracted');
675                     expect(oldItem2.children()[0].getText()).to.equal('2', 'second item extracted');
676                     expect(newList.is('list')).to.equal(true, 'list lies above extracted item');
677                     expect(newList.children().length).to.equal(1, 'list has now one child');
678                 });
679
680                 it('removes list if all its items are extracted', function() {
681                     var c = canvas.fromXML('\
682                         <section>\
683                             <div class="list.items">\
684                                 <div class="item">some item</div>\
685                                 <div class="item">some item 2</div>\
686                             </div>\
687                         </section>'),
688                         list = c.doc().children()[0],
689                         item1 = list.children()[0],
690                         item2 = list.children()[1];
691
692                     c.list.extractItems({element1: item1, element2: item2});
693
694                     var section = c.doc(),
695                         list1 = section.children()[0],
696                         oldItem1 = section.children()[0],
697                         oldItem2 = section.children()[1];
698
699                     expect(section.children().length).to.equal(2, 'section contains two children');
700                     expect(oldItem1.children()[0].getText()).to.equal('some item');
701                     expect(oldItem2.children()[0].getText()).to.equal('some item 2');
702                 });
703
704                 it('creates two lists with extracted items in the middle if extracting from the middle of the list - nested case' , function() {
705                     var c = canvas.fromXML('\
706                         <section>\
707                             <div class="list.items">\
708                                 <div class="item">0</div>\
709                                 <div class="item">\
710                                     <div class="list.items">\
711                                         <div class="item">1.1</div>\
712                                         <div class="item">1.2</div>\
713                                         <div class="item">1.3</div>\
714                                     </div>\
715                                 </div>\
716                                 <div class="item">2</div>\
717                             </div>\
718                         </section>'),
719                         list = c.doc().children()[0],
720                         nestedList = list.children()[1].children()[0],
721                         nestedListItem = nestedList.children()[1];
722
723                     c.list.extractItems({element1: nestedListItem, element2: nestedListItem});
724
725                     var section = c.doc(),
726                         list = section.children()[0],
727                         item1 = list.children()[0],
728                         item2 = list.children()[1], //
729                         item3 = list.children()[2],
730                         item4 = list.children()[3], //
731                         item5 = list.children()[4],
732                         nestedList1 = item2.children()[0],
733                         nestedList2 = item4.children()[0];
734
735                     expect(list.children().length).to.equal(5, 'top list has five items');
736                     
737                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
738
739                     expect(item2.getWlxmlClass()).to.equal('item', 'first nested list is still wrapped in item element');
740                     expect(nestedList1.children().length).to.equal(1, 'first nested list is left with one child');
741                     expect(nestedList1.children()[0].children()[0].getText()).to.equal('1.1', 'first nested list item left alone');
742                     
743                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
744
745                     expect(item4.getWlxmlClass()).to.equal('item', 'second nested list is still wrapped in item element');
746                     expect(nestedList2.children().length).to.equal(1, 'second nested list is left with one child');
747                     expect(nestedList2.children()[0].children()[0].getText()).to.equal('1.3', 'second nested list item left alone');
748
749                     expect(item5.children()[0].getText()).to.equal('2', 'last item ok');
750                 });
751
752                 it('puts extracted items below the list if ending item is the last one - nested case' , function() {
753                     var c = canvas.fromXML('\
754                         <section>\
755                             <div class="list.items">\
756                                 <div class="item">0</div>\
757                                 <div class="item">\
758                                     <div class="list.items">\
759                                         <div class="item">1.1</div>\
760                                         <div class="item">1.2</div>\
761                                         <div class="item">1.3</div>\
762                                     </div>\
763                                 </div>\
764                                 <div class="item">2</div>\
765                             </div>\
766                         </section>'),
767                         list = c.doc().children()[0],
768                         nestedList = list.children()[1].children()[0],
769                         nestedListItem1 = nestedList.children()[1],
770                         nestedListItem2 = nestedList.children()[2];
771
772                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
773
774                     var section = c.doc(),
775                         list = section.children()[0],
776                         item1 = list.children()[0],
777                         item2 = list.children()[1],
778                         item3 = list.children()[2],
779                         item4 = list.children()[3],
780                         item5 = list.children()[4];
781                     nestedList = item2.children()[0];
782
783                     expect(list.children().length).to.equal(5, 'top list has five items');
784                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
785                     expect(item2.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
786                     expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
787                     expect(nestedList.children()[0].children()[0].getText()).to.equal('1.1', 'nested list item left alone');
788                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
789                     expect(item4.children()[0].getText()).to.equal('1.3', 'fourth item ok');
790                     expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
791                 });
792
793                 it('puts extracted items above the list if starting item is the first one - nested case' , function() {
794                     var c = canvas.fromXML('\
795                         <section>\
796                             <div class="list.items">\
797                                 <div class="item">0</div>\
798                                 <div class="item">\
799                                     <div class="list.items">\
800                                         <div class="item">1.1</div>\
801                                         <div class="item">1.2</div>\
802                                         <div class="item">1.3</div>\
803                                     </div>\
804                                 </div>\
805                                 <div class="item">2</div>\
806                             </div>\
807                         </section>'),
808                         list = c.doc().children()[0],
809                         nestedList = list.children()[1].children()[0],
810                         nestedListItem1 = nestedList.children()[0],
811                         nestedListItem2 = nestedList.children()[1];
812
813                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
814
815                     var section = c.doc(),
816                         list = section.children()[0],
817                         item1 = list.children()[0],
818                         item2 = list.children()[1],
819                         item3 = list.children()[2],
820                         item4 = list.children()[3],
821                         item5 = list.children()[4];
822                     nestedList = item4.children()[0];
823
824                     expect(list.children().length).to.equal(5, 'top list has five items');
825                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
826                     expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
827                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
828                     
829                     expect(item4.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
830                     expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
831                     expect(nestedList.children()[0].children()[0].getText()).to.equal('1.3', 'nested list item left alone');
832                     expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
833                 });
834
835                 it('removes list if all its items are extracted - nested case', function() {
836                     var c = canvas.fromXML('\
837                         <section>\
838                             <div class="list.items">\
839                                 <div class="item">0</div>\
840                                 <div class="item">\
841                                     <div class="list.items">\
842                                         <div class="item">1.1</div>\
843                                         <div class="item">1.2</div>\
844                                     </div>\
845                                 </div>\
846                                 <div class="item">2</div>\
847                             </div>\
848                         </section>'),
849                         list = c.doc().children()[0],
850                         nestedList = list.children()[1].children()[0],
851                         nestedListItem1 = nestedList.children()[0],
852                         nestedListItem2 = nestedList.children()[1];
853
854                     c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
855
856                     var section = c.doc(),
857                         list = section.children()[0],
858                         item1 = list.children()[0],
859                         item2 = list.children()[1],
860                         item3 = list.children()[2],
861                         item4 = list.children()[3];
862
863                     expect(list.children().length).to.equal(4, 'top list has four items');
864                     expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
865                     expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
866                     expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
867                     expect(item4.children()[0].getText()).to.equal('2', 'fourth item ok');
868                 });
869
870                 it('extracts items out of outer most list when merge flag is set to false', function() {
871                     var c = canvas.fromXML('\
872                         <section>\
873                             <div class="list.items">\
874                                 <div class="item">0</div>\
875                                 <div class="item">\
876                                     <div class="list.items">\
877                                         <div class="item">1.1</div>\
878                                         <div class="item">1.2</div>\
879                                     </div>\
880                                 </div>\
881                                 <div class="item">2</div>\
882                             </div>\
883                         </section>'),
884                         section = c.doc(),
885                         list = section.children()[0],
886                         nestedList = list.children()[1].children()[0],
887                         nestedListItem = nestedList.children()[0];
888
889                     var test = c.list.extractItems({element1: nestedListItem, element2: nestedListItem, merge: false});
890
891                     expect(test).to.equal(true, 'extraction status ok');
892
893                     var sectionChildren = section.children(),
894                         extractedItem = sectionChildren[1];
895
896                     expect(sectionChildren.length).to.equal(3, 'section has three children');
897                     expect(sectionChildren[0].is('list')).to.equal(true, 'first child is a list');
898
899                     expect(extractedItem.getWlxmlTag()).to.equal('div', 'extracted item is a wlxml div');
900                     expect(extractedItem.getWlxmlClass()).to.equal(undefined, 'extracted item has no wlxml class');
901                     expect(extractedItem.children()[0].getText()).to.equal('1.1', 'extracted item ok');
902                     expect(sectionChildren[2].is('list')).to.equal(true, 'second child is a list');
903                 });
904             });
905         });
906
907     });
908
909     describe('Cursor', function() {
910
911         var getSelection;
912
913         beforeEach(function() {
914             getSelection = sinon.stub(window, 'getSelection');
915         });
916
917         afterEach(function() {
918             getSelection.restore();
919         });
920
921         it('returns position when browser selection collapsed', function() {
922             var c = canvas.fromXML('<section>Alice has a cat</section>'),
923                 dom = c.doc().dom(),
924                 text = dom.contents()[0];
925
926             expect($(text).text()).to.equal('Alice has a cat');
927
928             getSelection.returns({
929                 anchorNode: text,
930                 focusNode: text,
931                 anchorOffset: 5,
932                 focusOffset: 5,
933                 isCollapsed: true
934             });
935             var cursor = c.getCursor(),
936                 position = cursor.getPosition();
937
938             expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything');
939             expect(position.element.getText()).to.equal('Alice has a cat');
940             expect(position.offset).to.equal(5);
941         });
942
943         it('returns boundries of selection when browser selection not collapsed', function() {
944             var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
945                 dom = c.doc().dom(),
946                 text = {
947                     alice: dom.contents()[0],
948                     has: $(dom.contents()[1]).contents()[0],
949                     cat: dom.contents()[4]
950                 },
951                 cursor = c.getCursor(),
952                 aliceElement = c.getDocumentElement(text.alice),
953                 catElement = c.getDocumentElement(text.cat);
954
955
956                 [
957                     {focus: text.alice, focusOffset: 1, anchor: text.cat,   anchorOffset: 2, selectionAnchor: catElement},
958                     {focus: text.cat,   focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement}
959                 ].forEach(function(s, idx) {
960                     getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset});
961
962                     var selectionStart = cursor.getSelectionStart(),
963                         selectionEnd = cursor.getSelectionEnd(),
964                         selectionAnchor = cursor.getSelectionAnchor();
965
966                     expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting');
967                     expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx);
968                     expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx);
969                     expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx);
970                     expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx);
971                     expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok');
972                     expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok');
973                 });
974         });
975
976         it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() {
977             var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
978                 dom = c.doc().dom(),
979                 text = {
980                     alice: dom.contents()[0],
981                     has: $(dom.contents()[1]).contents()[0],
982                     a: dom.contents()[2],
983                     big: $(dom.contents()[3]).contents()[0],
984                     cat: dom.contents()[4]
985                 },
986                 cursor = c.getCursor();
987
988             expect($(text.alice).text()).to.equal('Alice ');
989             expect($(text.has).text()).to.equal('has');
990             expect($(text.a).text()).to.equal(' a ');
991             expect($(text.big).text()).to.equal('big');
992             expect($(text.cat).text()).to.equal(' cat');
993
994             getSelection.returns({anchorNode: text.alice, focusNode: text.a});
995             expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children');
996
997             getSelection.returns({anchorNode: text.alice, focusNode: text.cat});
998             expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children');
999
1000             getSelection.returns({anchorNode: text.alice, focusNode: text.has});
1001             expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children');
1002
1003             getSelection.returns({anchorNode: text.has, focusNode: text.big});
1004             expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children');
1005             
1006         })
1007     });
1008 });
1009
1010
1011 });