5 'modules/documentCanvas/canvas/canvas',
 
   6 'modules/documentCanvas/canvas/utils',
 
   7 'modules/documentCanvas/canvas/documentElement',
 
   9 ], function($, chai, sinon, canvas, utils, documentElement, wlxml) {
 
  12 /* global describe, it, beforeEach, afterEach */
 
  14 var expect = chai.expect;
 
  16 var getCanvasFromXML = function(xml, elements) {
 
  17     return canvas.fromXMLDocument(getDocumentFromXML(xml), elements);
 
  20 var getDocumentFromXML = function(xml) {
 
  21     return wlxml.WLXMLDocumentFromXML(xml);
 
  24 var wait = function(callback, timeout) {
 
  26     return window.setTimeout(callback, timeout || 0.5);
 
  30 describe('wtf', function() {
 
  31     it('wtf!', function() {
 
  32         var c = getCanvasFromXML('<section>Alice</section>'),
 
  33             doc = c.wlxmlDocument;
 
  35         var txtNode = doc.root.contents()[0];
 
  36         txtNode.wrapWith({tagName: 'header', start: 1, end: 2});
 
  37         expect(c.doc().children().length).to.equal(3);
 
  41 describe('new Canvas', function() {
 
  42     it('abc', function() {
 
  43         var doc = wlxml.WLXMLDocumentFromXML('<section>Alice <span>has</span> a cat!</div>'),
 
  44             c = canvas.fromXMLDocument(doc);
 
  46         expect(c.doc().children()).to.have.length(3);
 
  47         expect(c.doc().children()[0].canvas).to.equal(c);
 
  48         expect(c.doc().children()[0].wlxmlNode.sameNode(doc.root));
 
  52 describe('Handling empty text nodes', function() {
 
  53     it('puts zero width space into node with about to be remove text', function(done) {
 
  54         var c = getCanvasFromXML('<section>Alice</section>'),
 
  55             textElement = c.doc().children()[0];
 
  56         textElement.setText('');
 
  58         /* Wait for MutationObserver to kick in. */
 
  60             expect(textElement.getText({raw:true})).to.equal(utils.unicode.ZWS, 'ZWS in canvas');
 
  61             expect(c.wlxmlDocument.root.contents()[0].getText()).to.equal('', 'empty string in a document');
 
  67 describe('Handling changes to the document', function() {
 
  68     it('replaces the whole canvas content when document root node replaced', function() {
 
  69         var doc = getDocumentFromXML('<section></section>'),
 
  70             c = canvas.fromXMLDocument(doc);
 
  72         var header = doc.root.replaceWith({tagName: 'header'});
 
  73         expect(c.doc().wlxmlNode.sameNode(header)).to.equal(true);
 
  77 describe('Listening to document changes', function() {
 
  79     it('Handling element node moved', function() {
 
  80         var doc = getDocumentFromXML('<section><a></a><b></b></section>'),
 
  81             a = doc.root.contents()[0],
 
  82             b = doc.root.contents()[1],
 
  83             c = canvas.fromXMLDocument(doc);
 
  86         var sectionChildren = c.doc().children();
 
  87         expect(sectionChildren.length).to.equal(2);
 
  88         expect(sectionChildren[0].wlxmlNode.getTagName()).to.equal('b');
 
  89         expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a');
 
  92     it('Handling text node moved', function() {
 
  93         var doc = getDocumentFromXML('<section><a></a>Alice</section>'),
 
  94             a = doc.root.contents()[0],
 
  95             textNode = doc.root.contents()[1],
 
  96             c = canvas.fromXMLDocument(doc);
 
  99         var sectionChildren = c.doc().children();
 
 100         expect(sectionChildren.length).to.equal(2);
 
 101         expect(sectionChildren[0].getText()).to.equal('Alice');
 
 102         expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a');
 
 105     it('Handles nodeTagChange event', function() {
 
 107         var doc = wlxml.WLXMLDocumentFromXML('<section><div>Alice</div></section>'),
 
 108             c = canvas.fromXMLDocument(doc);
 
 110         doc.root.contents()[0].setTag('header');
 
 112         var headerNode = doc.root.contents()[0],
 
 113             headerElement = c.doc().children()[0];
 
 115         expect(headerElement.wlxmlNode.getTagName()).to.equal('header', 'element ok');
 
 117         /* Make sure we handle invalidation of reference to wlxmlNode after changing its tag */
 
 118         expect(headerNode.getData('canvasElement').sameNode(headerElement)).to.equal(true, 'node->element');
 
 119         expect(headerElement.wlxmlNode.sameNode(headerNode)).to.equal(true, 'element->node');
 
 122     it('Handles nodeDetached event for an empty text node', function(done) {
 
 123         var doc = wlxml.WLXMLDocumentFromXML('<section><div>A<span>b</span></div></section>'),
 
 124             aTextNode = doc.root.contents()[0].contents()[0],
 
 127         canvas.fromXMLDocument(doc);
 
 128         aTextElement = utils.getElementForTextNode(aTextNode);
 
 130         aTextElement.setText('');
 
 133             var parent = aTextElement.parent();
 
 134             expect(aTextElement.getText({raw:true})).to.equal(utils.unicode.ZWS, 'canvas represents this as empty node');
 
 135             aTextElement.wlxmlNode.detach();
 
 136             expect(parent.children().length).to.equal(1);
 
 137             expect(parent.children()[0].wlxmlNode.getTagName()).to.equal('span');
 
 143 describe('Displaying span nodes', function() {
 
 144     it('inlines a span element with a text', function() {
 
 145         var c = getCanvasFromXML('<section><span>Alice</span></section>'),
 
 146             spanElement = c.doc().children()[0];
 
 147         expect(spanElement.isBlock()).to.equal(false);
 
 149     it('renders non-span element as a block', function() {
 
 150         var c = getCanvasFromXML('<section><span></span></section>'),
 
 151             element = c.doc().children()[0],
 
 152             node = element.wlxmlNode;
 
 154         expect(element.isBlock()).to.equal(false, 'initially inline');
 
 155         node = node.setTag('div');
 
 156         expect(node.getData('canvasElement').isBlock()).to.equal(true, 'block');
 
 159     it('inlines a span element if its block content gets removed', function() {
 
 160         var c = getCanvasFromXML('<section><span>Alice <div>has</div> a cat!</span></section>'),
 
 161             spanElement = c.doc().children()[0],
 
 162             divNode = spanElement.wlxmlNode.contents()[1];
 
 164         expect(spanElement.isBlock()).to.equal(true, 'initially a block');
 
 166         expect(spanElement.isBlock()).to.equal(false, 'inlined after removing inner block');
 
 168         spanElement.wlxmlNode.append({tagName: 'div'});
 
 170         expect(spanElement.isBlock()).to.equal(true, 'block again after bringing back inner block');
 
 173     it('keeps showing element as a block after changing its node tag to span if it contains elements of non-span nodes', function() {
 
 174         var c = getCanvasFromXML('<section><div><div></div></div></section>'),
 
 175             outerDivElement = c.doc().children()[0],
 
 176             outerDivNode = outerDivElement.wlxmlNode;
 
 177         outerDivNode = outerDivNode.setTag('span');
 
 178         expect(c.doc().children()[0].isBlock()).to.equal(true);
 
 183 describe('Default document changes handling', function() {
 
 184     it('handles added node', function() {
 
 185         var c = getCanvasFromXML('<section></section>');
 
 186         c.wlxmlDocument.root.append({tagName:'div'});
 
 187         expect(c.doc().children().length).to.equal(1);
 
 188         c.wlxmlDocument.root.prepend({tagName:'div'});
 
 189         expect(c.doc().children().length).to.equal(2);
 
 191         var node = c.wlxmlDocument.root.contents()[1];
 
 192         node.before({tagName: 'div'});
 
 193         expect(c.doc().children().length).to.equal(3);
 
 194         node.after({tagName: 'div'});
 
 195         expect(c.doc().children().length).to.equal(4);
 
 198     it('handles attribute value change for a class attribute', function() {
 
 199         var c = getCanvasFromXML('<section></section>');
 
 200         c.wlxmlDocument.root.setAttr('class', 'test');
 
 201         expect(c.doc().wlxmlNode.getClass()).to.equal('test');
 
 204     it('handles detached node', function() {
 
 205         var c = getCanvasFromXML('<section><div></div></section>');
 
 206         c.wlxmlDocument.root.contents()[0].detach();
 
 207         expect(c.doc().children().length).to.equal(0);
 
 210     it('handles moved node', function() {
 
 211         var doc = getDocumentFromXML('<section><a></a><b></b></section>'),
 
 212             a = doc.root.contents()[0],
 
 213             b = doc.root.contents()[1],
 
 214             c = canvas.fromXMLDocument(doc);
 
 217         var sectionChildren = c.doc().children();
 
 218         expect(sectionChildren.length).to.equal(2);
 
 219         expect(sectionChildren[0].wlxmlNode.getTagName()).to.equal('b');
 
 220         expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a');
 
 223     it('handles change in a text node', function() {
 
 224         var c = getCanvasFromXML('<section>Alice</section>');
 
 225         c.wlxmlDocument.root.contents()[0].setText('cat');
 
 226         expect(c.doc().children()[0].getText()).to.equal('cat');
 
 230 describe('Custom elements based on wlxml class attribute', function() {
 
 231     it('allows custom rendering', function() {
 
 232         var prototype = $.extend({}, documentElement.DocumentNodeElement.prototype, {
 
 234                     this._container().append('<test></test>');
 
 237             c = getCanvasFromXML('<section><div class="testClass"></div></section>', [
 
 238             {tag: 'div', klass: 'testClass', prototype: prototype}
 
 241         expect(c.doc().children()[0]._container().children('test').length).to.equal(1); // @!
 
 244     it('allows handling changes to internal structure of rendered node', function() {
 
 245         var prototype = $.extend({}, documentElement.DocumentNodeElement.prototype, {
 
 247                     this.header = $('<h1>');
 
 248                     this._container().append(this.header);
 
 251                 refresh2: function() {
 
 252                     this.header.text(this.wlxmlNode.contents().length);
 
 254                 onNodeAdded: function(event) {
 
 260         var c = getCanvasFromXML('<section><div class="testClass"><a></a></div></section>', [
 
 261             {tag: 'div', klass: 'testClass', prototype: prototype}
 
 264         var node = c.wlxmlDocument.root.contents()[0],
 
 265             element = node.getData('canvasElement');
 
 267         var header = element.dom.find('h1');
 
 268         expect(header.text()).to.equal('1', 'just <a>');
 
 270         node.append({tagName: 'div'});
 
 272         expect(header.text()).to.equal('2', 'added div');
 
 275     describe('Handling unknown class', function() {
 
 276         it('Inherits default behavior', function() {
 
 277             var c = getCanvasFromXML('<section><div class="unknown">Hi!</div></section>');
 
 278             expect(c.doc().children()[0].children()[0].getText()).to.equal('Hi!');
 
 283 describe('Cursor', function() {
 
 287     var findTextNode = function(inside, text) {
 
 288         var nodes = inside.find(':not(iframe)').addBack().contents().filter(function() {
 
 289             return this.nodeType === Node.TEXT_NODE && this.data === text;
 
 297     beforeEach(function() {
 
 299         getSelection = sinon.stub(window, 'getSelection');
 
 302     afterEach(function() {
 
 303         getSelection.restore();
 
 306     it('returns position when browser selection collapsed', function() {
 
 307         var c = getCanvasFromXML('<section>Alice has a cat</section>'),
 
 309             text = findTextNode(dom, 'Alice has a cat');
 
 311         expect(text.nodeType).to.equal(Node.TEXT_NODE, 'correct node selected');
 
 312         expect($(text).text()).to.equal('Alice has a cat');
 
 314         getSelection.returns({
 
 321         var cursor = c.getCursor(),
 
 322             position = cursor.getPosition();
 
 324         expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything');
 
 325         expect(position.element.getText()).to.equal('Alice has a cat');
 
 326         expect(position.offset).to.equal(5);
 
 327         expect(position.offsetAtEnd).to.equal(false, 'offset is not at end');
 
 329         getSelection.returns({
 
 337         expect(cursor.getPosition().offsetAtEnd).to.equal(true, 'offset at end');
 
 340     it('recognizes selection start and end on document order', function() {
 
 341         var c = getCanvasFromXML('<section><span>Alice</span><span>has a cat</span><div>abc<span>...</span>cde</div></section>'),
 
 343             textFirst = findTextNode(dom, 'Alice'),
 
 344             textSecond = findTextNode(dom, 'has a cat'),
 
 345             textAbc = findTextNode(dom, 'abc'),
 
 346             textCde = findTextNode(dom, 'cde');
 
 348         var check = function(label, expected) {
 
 349             var cursor = c.getCursor();
 
 350             label = label + ': ';
 
 351             expect(cursor.getSelectionStart().element.getText()).to.equal(expected.start.text, label + 'start element ok');
 
 352             expect(cursor.getSelectionStart().offset).to.equal(expected.start.offset, label + 'start offset ok');
 
 353             expect(cursor.getSelectionEnd().element.getText()).to.equal(expected.end.text, label + 'end element ok');
 
 354             expect(cursor.getSelectionEnd().offset).to.equal(expected.end.offset, label + 'end offset ok');
 
 357         getSelection.returns({
 
 358             anchorNode: textFirst,
 
 359             focusNode: textFirst,
 
 365         check('same element, anchor first', {
 
 366             start: {text: 'Alice', offset: 1},
 
 367             end: {text: 'Alice', offset:3}
 
 371         getSelection.returns({
 
 372             anchorNode: textFirst,
 
 373             focusNode: textFirst,
 
 379         check('same element, anchor second', {
 
 380             start: {text: 'Alice', offset: 1},
 
 381             end: {text: 'Alice', offset:3}
 
 385         getSelection.returns({
 
 393         check('same parent, anchor first', {
 
 394             start: {text: 'abc', offset: 3},
 
 395             end: {text: 'cde', offset:1}
 
 399         getSelection.returns({
 
 407         check('same parent, anchor second', {
 
 408             start: {text: 'abc', offset: 3},
 
 409             end: {text: 'cde', offset:1}
 
 413         getSelection.returns({
 
 414             anchorNode: textFirst,
 
 415             focusNode: textSecond,
 
 421         check('different parents, anchor first', {
 
 422             start: {text: 'Alice', offset: 1},
 
 423             end: {text: 'has a cat', offset:3}
 
 427         getSelection.returns({
 
 428             anchorNode: textSecond,
 
 429             focusNode: textFirst,
 
 435         check('different parents, anchor second', {
 
 436             start: {text: 'Alice', offset: 1},
 
 437             end: {text: 'has a cat', offset:3}
 
 441     it('returns boundries of selection when browser selection not collapsed', function() {
 
 442         var c = getCanvasFromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
 
 445                 alice: findTextNode(dom, 'Alice '),
 
 446                 has: findTextNode(dom, 'has'),
 
 447                 cat: findTextNode(dom, ' cat')
 
 449             cursor = c.getCursor(),
 
 450             aliceElement = c.getDocumentElement(text.alice),
 
 451             catElement = c.getDocumentElement(text.cat);
 
 454             {focus: text.alice, focusOffset: 1, anchor: text.cat,   anchorOffset: 2, selectionAnchor: catElement},
 
 455             {focus: text.cat,   focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement}
 
 456         ].forEach(function(s, idx) {
 
 457             getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset});
 
 459             var selectionStart = cursor.getSelectionStart(),
 
 460                 selectionEnd = cursor.getSelectionEnd(),
 
 461                 selectionAnchor = cursor.getSelectionAnchor();
 
 463             expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting');
 
 464             expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx);
 
 465             expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx);
 
 466             expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx);
 
 467             expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx);
 
 468             expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok');
 
 469             expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok');
 
 473     it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() {
 
 474         var c = getCanvasFromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
 
 477                 alice: findTextNode(dom, 'Alice '),
 
 478                 has: findTextNode(dom, 'has'),
 
 479                 a: findTextNode(dom, ' a '),
 
 480                 big: findTextNode(dom, 'big'),
 
 481                 cat: findTextNode(dom, ' cat'),
 
 483             cursor = c.getCursor();
 
 485         expect($(text.alice).text()).to.equal('Alice ');
 
 486         expect($(text.has).text()).to.equal('has');
 
 487         expect($(text.a).text()).to.equal(' a ');
 
 488         expect($(text.big).text()).to.equal('big');
 
 489         expect($(text.cat).text()).to.equal(' cat');
 
 491         getSelection.returns({anchorNode: text.alice, focusNode: text.a});
 
 492         expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children');
 
 494         getSelection.returns({anchorNode: text.alice, focusNode: text.cat});
 
 495         expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children');
 
 497         getSelection.returns({anchorNode: text.alice, focusNode: text.has});
 
 498         expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children');
 
 500         getSelection.returns({anchorNode: text.has, focusNode: text.big});
 
 501         expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children');