1 define(function(require) {
 
   4 /* globals describe, it */
 
   6 var _ = require('libs/underscore'),
 
   7     chai = require('libs/chai'),
 
   8     sinon = require('libs/sinon'),
 
   9     wlxml = require('wlxml/wlxml'),
 
  10     canvas = require('modules/documentCanvas/canvas/canvas'),
 
  11     keyboard = require('modules/documentCanvas/canvas/keyboard'),
 
  12     keyEvent = require('modules/documentCanvas/canvas/keyEvent'),
 
  13     corePlugin = require('./core.js'),
 
  16 var K = keyboard.KEYS;
 
  18 var getDocumentFromXML = function(xml, options) {
 
  19     var doc = wlxml.WLXMLDocumentFromXML(xml, options || {});
 
  20     doc.registerExtension(corePlugin.documentExtension);
 
  25 var getCanvasFromXML = function(xml, elements) {
 
  26     return canvas.fromXMLDocument(getDocumentFromXML(xml), elements);
 
  29 var getTextNodes = function(text, doc) {
 
  32     var search = function(node) {
 
  33         node.contents().forEach(function(node) {
 
  34             if(node.nodeType === Node.TEXT_NODE) {
 
  35                 if(node.getText() === text) {
 
  47 var getTextNode = function(text, doc) {
 
  48     var nodes = getTextNodes(text, doc),
 
  50     if(nodes.length === 0) {
 
  51         error = 'Text not found';
 
  52     } else if(nodes.length > 1) {
 
  53         error = 'Text not unique';
 
  54     } else if(nodes[0].getText() !== text) {
 
  55         error = 'I was trying to cheat your test :(';
 
  58         throw new Error(error);
 
  63 var getTextElement = function(text, c) {
 
  64     var node = getTextNode(text, c.wlxmlDocument),
 
  65         element =  node && node.getData('canvasElement');
 
  66     if(!(element && element.getText() === text)) {
 
  73 describe('Document extensions', function() {
 
  74     describe('break content', function() {
 
  75         it('break text into two nodes', function() {
 
  76             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
 
  77                 textNode = doc.root.contents()[0].contents()[0];
 
  79             var result = textNode.breakContent({offset:3});
 
  81             var section = doc.root;
 
  82             expect(section.contents().length).to.equal(2);
 
  83             expect(section.contents()[0].contents()[0].getText()).to.equal('Ali');
 
  84             expect(section.contents()[1].contents()[0].getText()).to.equal('ce');
 
  86             expect(result.first.sameNode(section.contents()[0])).to.equal(true);
 
  87             expect(result.second.sameNode(section.contents()[1])).to.equal(true);
 
  88             expect(result.emptyText).to.equal(undefined, 'no new text node created');
 
  90         it('puts empty text node when breaking at the very beginning', function() {
 
  91             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
 
  92                 textNode = doc.root.contents()[0].contents()[0];
 
  94             var result = textNode.breakContent({offset:0}),
 
  95                 firstNode = doc.root.contents()[0];
 
  97             expect(result.emptyText.sameNode(firstNode.contents()[0]));
 
  98             expect(result.emptyText.getText()).to.equal('');
 
 100         it('puts empty text node when breaking at the very end', function() {
 
 101             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
 
 102             textNode = doc.root.contents()[0].contents()[0];
 
 104             var result = textNode.breakContent({offset:5}),
 
 105                 secondNode = doc.root.contents()[1];
 
 107             expect(result.emptyText.sameNode(secondNode.contents()[0]));
 
 108             expect(result.emptyText.getText()).to.equal('');
 
 112     describe('mergin text with preceding content', function() {
 
 113         it('does nothing if text node parent has no preceding element', function() {
 
 114             var doc = getDocumentFromXML('<section><div><div>some text</div></div></section>'),
 
 115                 text = getTextNode('some text', doc),
 
 118             doc.on('change', spy);
 
 119             text.mergeContentUp();
 
 120             expect(spy.callCount).to.equal(0);
 
 122         it('does nothing if text node parent is precedeed by text node', function() {
 
 123             var doc = getDocumentFromXML('<section><div></div>another text<div>some text</div></section>'),
 
 124                 text = getTextNode('some text', doc),
 
 127             doc.on('change', spy);
 
 128             text.mergeContentUp();
 
 129             expect(spy.callCount).to.equal(0);
 
 131         it('does nothing if text node is not first child of its parent', function() {
 
 132             var doc = getDocumentFromXML('<section><div></div><div><a></a>some text</div></section>'),
 
 133                 text = getTextNode('some text', doc),
 
 136             doc.on('change', spy);
 
 137             text.mergeContentUp();
 
 138             expect(spy.callCount).to.equal(0);
 
 140         it('moves text node and its siblings to the block element preceding text node parent', function() {
 
 141             var doc = getDocumentFromXML('<section><div></div><div>some text<span>is</span> here!</div></section>'),
 
 142                 text = getTextNode('some text', doc);
 
 144             text.mergeContentUp();
 
 146             var contents = doc.root.contents();
 
 147             expect(contents.length).to.equal(1);
 
 148             expect(contents[0].contents().length).to.equal(3);
 
 149             expect(contents[0].contents()[0].getText()).to.equal('some text');
 
 150             expect(contents[0].contents()[1].getTagName()).to.equal('span');
 
 151             expect(contents[0].contents()[2].getText()).to.equal(' here!');
 
 156 describe.only('Keyboard interactions', function() {
 
 158     var Keyboard = function(canvas) {
 
 159         this.canvas = canvas;
 
 162     _.extend(Keyboard.prototype, {
 
 163         press: function(key) {
 
 164             this.canvas.triggerKeyEvent(keyEvent.fromParams({key:key}), this.selection);
 
 165             this.selection = this.canvas.getSelection();
 
 168         withCaret: function(where) {
 
 169             var offset = where.indexOf('|'),
 
 170                 text = where.split('|').join(''),
 
 171                 el = getTextElement(text, this.canvas),
 
 172                 selection = this.canvas.createSelection({type: 'caret', element: el, offset: offset});
 
 174                 throw new Error('Invalid caret');
 
 176             this.selection = selection;
 
 179         withSelection: function(start, end) {
 
 180             var startOffset = start.indexOf('|'),
 
 181                 endOffset = end.indexOf('|'),
 
 182                 startText= start.split('|').join(''),
 
 183                 endText = end.split('|').join(''),
 
 184                 startElement = getTextElement(startText, this.canvas),
 
 185                 endElement = getTextElement(endText, this.canvas),
 
 186                 selection = this.canvas.createSelection({
 
 187                     type: 'textSelection', 
 
 188                     anchorElement: startElement,
 
 189                     anchorOffset: startOffset,
 
 190                     focusElement: endElement,
 
 191                     focusOffset: endOffset
 
 193             if(startOffset === -1 || endOffset === -1) {
 
 194                 throw new Error('Invalid text selection');
 
 196             this.selection = selection;
 
 201     describe('deleting text with selection', function() {
 
 202         [K.BACKSPACE, K.DELETE].forEach(function(key) {
 
 203             it('deletes text withing a single text element ' + key, function() {
 
 204                 var c = getCanvasFromXML('<section><div>Alice</div></section>'),
 
 207                 k.withSelection('A|lice', 'Alic|e').press(key);
 
 208                 expect(c.wlxmlDocument.root.contents()[0].contents()[0].getText()).to.equal('Ae');
 
 210             it('deletes text across two paragraphs ' + key, function() {
 
 211                 var c = getCanvasFromXML('<section><div class="p">Alice</div><div class="p">cat</div></section>'),
 
 214                 k.withSelection('A|lice', 'c|at').press(key);
 
 215                 var rootContents = c.wlxmlDocument.root.contents();
 
 217                 expect(rootContents.length).to.equal(2);
 
 218                 expect(rootContents[0].contents()[0].getText()).to.equal('A');
 
 219                 expect(rootContents[1].contents()[0].getText()).to.equal('at');
 
 222             it('keeps an empty paragraph after deleting its whole text ' + key, function() {
 
 223                 var c = getCanvasFromXML('<section><div class="p">Alice</div></section>'),
 
 226                 k.withSelection('|Alice', 'Alice|').press(key);
 
 227                 var rootContents = c.wlxmlDocument.root.contents();
 
 229                 expect(rootContents.length).to.equal(1);
 
 230                 expect(rootContents[0].contents()[0].getText()).to.equal('');
 
 236     // describe('deleting with a caret', function() {
 
 237     //     it('keeps an empty paragraph after deleteing last letter with backspace', function() {
 
 238     //         var c = getCanvasFromXML('<section><div class="p">A</div></section>'),
 
 239     //             k = new Keyboard(c);
 
 241     //         k.withCaret('A|').press(K.BACKSPACE);
 
 242     //         var rootContents = c.wlxmlDocument.root.contents();
 
 244     //         expect(rootContents.length).to.equal(1);
 
 245     //         expect(rootContents[0].contents()[0].getText()).to.equal('');    
 
 247     //     // it('removes a paragraph on yet another delete' + key, function() {
 
 253             // + empty when bck/ins + l===1
 
 255     describe('backspace at the beginning', function() {
 
 256         it('merges two adjacent paragraphs', function() {
 
 257             var c = getCanvasFromXML('<section><div class="p">A</div><div class="p">B</div></section>'),
 
 260             k.withCaret('|B').press(K.BACKSPACE);
 
 262             var rootContents = c.wlxmlDocument.root.contents();
 
 263             expect(rootContents.length).to.equal(1);
 
 264             expect(rootContents[0].getClass()).to.equal('p');
 
 265             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
 
 267         it('merges a paragraph with a header', function() {
 
 268             var c = getCanvasFromXML('<section><header>A</header><div class="p">B</div></section>'),
 
 271             k.withCaret('|B').press(K.BACKSPACE);
 
 273             var rootContents = c.wlxmlDocument.root.contents();
 
 274             expect(rootContents.length).to.equal(1);
 
 275             expect(rootContents[0].getTagName()).to.equal('header');
 
 276             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
 
 278         it('merges two adjacent headers', function() {
 
 279             var c = getCanvasFromXML('<section><header>A</header><header>B</header></section>'),
 
 282             k.withCaret('|B').press(K.BACKSPACE);
 
 283             var rootContents = c.wlxmlDocument.root.contents();
 
 284             expect(rootContents.length).to.equal(1);
 
 285             expect(rootContents[0].getTagName()).to.equal('header');
 
 286             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
 
 288         it('merges a header with a paragraph', function() {
 
 289             var c = getCanvasFromXML('<section><div class="p">A</div><header>B</header></section>'),
 
 292             k.withCaret('|B').press(K.BACKSPACE);
 
 294             var rootContents = c.wlxmlDocument.root.contents();
 
 295             expect(rootContents.length).to.equal(1);
 
 296             expect(rootContents[0].is('p')).to.equal(true);
 
 297             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
 
 299         it('merges a paragraph into a last list item', function() {
 
 300             var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div><div class="p">paragraph</div></section>'),
 
 301                 list = c.wlxmlDocument.root.contents()[0],
 
 304             k.withCaret('|paragraph').press(K.BACKSPACE);
 
 306             var rootContents = c.wlxmlDocument.root.contents();
 
 307             expect(rootContents.length).to.equal(1);
 
 308             expect(rootContents[0].sameNode(list)).to.equal(true);
 
 310             var items = list.contents();
 
 311             expect(items.length).to.equal(1);
 
 312             expect(items[0].contents()[0].getText()).to.equal('itemparagraph');
 
 314         it('merges a list item with a list item', function() {
 
 315             var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
 
 316                 list = c.wlxmlDocument.root.contents()[0],
 
 319             k.withCaret('|item2').press(K.BACKSPACE);
 
 321             var rootContents = c.wlxmlDocument.root.contents();
 
 322             expect(rootContents.length).to.equal(1);
 
 324             expect(rootContents[0].sameNode(list)).to.equal(true);
 
 326             var items = list.contents();
 
 328             expect(items.length).to.equal(1);
 
 329             expect(items[0].contents()[0].getText()).to.equal('item1item2');
 
 331         it('creates a new paragraph preceding the list from a first list item', function() {
 
 332             var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
 
 333                 list = c.wlxmlDocument.root.contents()[0],
 
 336             k.withCaret('|item1').press(K.BACKSPACE);
 
 338             var rootContents = c.wlxmlDocument.root.contents();
 
 339             expect(rootContents.length).to.equal(2);
 
 341             expect(rootContents[0].getClass()).to.equal('p');
 
 342             expect(rootContents[0].contents()[0].getText()).to.equal('item1');
 
 344             expect(rootContents[1].sameNode(list)).to.equal(true);
 
 346         it('removes list after moving up its only item', function() {
 
 347             var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div></section>'),
 
 350             k.withCaret('|item').press(K.BACKSPACE);
 
 351             var rootContents = c.wlxmlDocument.root.contents();
 
 352             expect(rootContents.length).to.equal(1);
 
 354             expect(rootContents[0].getClass()).to.equal('p');
 
 355             expect(rootContents[0].contents()[0].getText()).to.equal('item');