diff --git a/spec/cursor.spec.js b/spec/cursor.spec.js
index 654faa4d..81f95f94 100644
--- a/spec/cursor.spec.js
+++ b/spec/cursor.spec.js
@@ -4,10 +4,10 @@ describe('Cursor', function() {
expect(Cursor).toBeDefined();
});
- describe('with a range', function() {
+ describe('with a collapsed range at the end', function() {
beforeEach(function() {
- this.oneWord = $('
foobar
')[0];
+ this.oneWord = $('foobar
')[0];
this.range = rangy.createRange();
this.range.selectNodeContents(this.oneWord);
this.range.collapse(false);
@@ -25,6 +25,32 @@ describe('Cursor', function() {
expect(this.range.startOffset).toEqual(1);
expect(this.range.endOffset).toEqual(1);
});
+
+ describe('isAtEnd()', function() {
+ it('is true', function() {
+ expect(this.cursor.isAtEnd()).toBe(true);
+ });
+ });
+
+ describe('isAtBeginning()', function() {
+ it('is false', function() {
+ expect(this.cursor.isAtBeginning()).toBe(false);
+ });
+ });
+
+ describe('save() and restore()', function() {
+
+ it('saves and restores the cursor', function() {
+ this.cursor.save();
+
+ // move the cursor so we can check the restore method.
+ this.cursor.moveAtBeginning();
+ expect(this.cursor.isAtBeginning()).toBe(true);
+
+ this.cursor.restore();
+ expect(this.cursor.isAtEnd()).toBe(true);
+ });
+ });
});
});
diff --git a/spec/parser.spec.js b/spec/parser.spec.js
index b147d59b..5f6883ad 100644
--- a/spec/parser.spec.js
+++ b/spec/parser.spec.js
@@ -31,6 +31,28 @@ describe('Parser', function() {
var linkWithSpan = $('')[0];
+ describe('getHost()', function() {
+
+ beforeEach(function() {
+ this.$host = $('');
+ });
+
+ it('works if host is passed', function() {
+ expect( parser.getHost(this.$host[0]) ).toBe( this.$host[0] );
+ });
+
+ it('works if a child of host is passed', function() {
+ this.$host.html('ab');
+ expect( parser.getHost(this.$host.find('em')[0]) ).toBe( this.$host[0] );
+ });
+
+ it('works if a text node is passed', function() {
+ this.$host.html('ab');
+ expect( parser.getHost(this.$host[0].firstChild) ).toBe( this.$host[0] );
+ });
+ });
+
+
describe('getNodeIndex()', function() {
it('gets element index of link in text', function() {
diff --git a/src/behavior.js b/src/behavior.js
index 51237085..50ddb629 100644
--- a/src/behavior.js
+++ b/src/behavior.js
@@ -119,9 +119,9 @@ var behavior = (function() {
return;
if(container.childNodes.length > 0)
- cursor.moveAfter(container.lastChild);
+ cursor.moveAtEnd(container);
else
- cursor.moveBefore(container);
+ cursor.moveAtBeginning(container);
cursor.setSelection();
fragment = document.createDocumentFragment();
@@ -133,10 +133,11 @@ var behavior = (function() {
merger.parentNode.removeChild(merger);
- range = rangeSaveRestore.save(cursor.range);
+ cursor.save();
content.normalizeTags(container);
content.normalizeSpaces(container);
- rangeSaveRestore.restore(element, range);
+ cursor.restore();
+ cursor.setSelection();
},
empty: function(element) {
@@ -151,15 +152,15 @@ var behavior = (function() {
switch(direction) {
case 'before':
previous = block.previous(element);
- if(previous) {
- cursor.moveAfter(previous);
+ if (previous) {
+ cursor.moveAtEnd(previous);
cursor.setSelection();
}
break;
case 'after':
next = block.next(element);
- if(next) {
- cursor.moveBefore(next);
+ if (next) {
+ cursor.moveAtBeginning(next);
cursor.setSelection();
}
break;
diff --git a/src/cursor.js b/src/cursor.js
index 65e48ab3..0044b565 100644
--- a/src/cursor.js
+++ b/src/cursor.js
@@ -118,13 +118,55 @@ var Cursor = (function() {
},
moveBefore: function(element) {
- this.range.setStart(element, 0);
- this.range.setEnd(element, 0);
+ this.setHost(element);
+ this.range.setStartBefore(element);
+ this.range.setEndBefore(element);
+ if (this.isSelection) return new Cursor(this.host, this.range);
},
moveAfter: function(element) {
+ this.setHost(element);
+ this.range.setStartAfter(element);
+ this.range.setEndAfter(element);
+ if (this.isSelection) return new Cursor(this.host, this.range);
+ },
+
+ moveAtBeginning: function(element) {
+ if (!element) element = this.host;
+ this.setHost(element);
+ this.range.selectNodeContents(element);
+ this.range.collapse(true);
+ if (this.isSelection) return new Cursor(this.host, this.range);
+ },
+
+ moveAtEnd: function(element) {
+ if (!element) element = this.host;
+ this.setHost(element);
this.range.selectNodeContents(element);
this.range.collapse(false);
+ if (this.isSelection) return new Cursor(this.host, this.range);
+ },
+
+ setHost: function(element) {
+ this.host = parser.getHost(element);
+ if (!this.host) {
+ error('Can not set cursor outside of an editable block');
+ }
+ },
+
+ save: function() {
+ this.savedRangeInfo = rangeSaveRestore.save(this.range);
+ this.savedRangeInfo.host = this.host;
+ },
+
+ restore: function() {
+ if (this.savedRangeInfo) {
+ this.host = this.savedRangeInfo.host;
+ this.range = rangeSaveRestore.restore(this.host, this.savedRangeInfo);
+ this.savedRangeInfo = undefined;
+ } else {
+ error('Could not restore selection');
+ }
},
equals: function(cursor) {
diff --git a/src/dispatcher.js b/src/dispatcher.js
index d9c558bd..f96e4093 100644
--- a/src/dispatcher.js
+++ b/src/dispatcher.js
@@ -57,7 +57,7 @@ var dispatcher = (function() {
return;
cursor = selectionWatcher.getSelection();
- if (cursor.isSelection) return;
+ if (!cursor || cursor.isSelection) return;
// Detect if the browser moved the cursor in the next tick.
// If the cursor stays at its position, fire the switch event.
diff --git a/src/parser.js b/src/parser.js
index be60b99a..e48df64d 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -31,6 +31,19 @@ var parser = (function() {
*/
return {
+ /**
+ * Get the editableJS host block of a node.
+ *
+ * @method getHost
+ * @param {DOM Node}
+ * @return {DOM Node}
+ */
+ getHost: function(node) {
+ var editableSelector = '.' + config.editableClass;
+ var hostNode = $(node).closest(editableSelector);
+ return hostNode.length ? hostNode[0] : undefined;
+ },
+
/**
* Get the index of a node.
* So that parent.childNodes[ getIndex(node) ] would return the node again
diff --git a/src/selection-watcher.js b/src/selection-watcher.js
index 670cbf8e..a1b3d1cd 100644
--- a/src/selection-watcher.js
+++ b/src/selection-watcher.js
@@ -23,10 +23,9 @@ var selectionWatcher = (function() {
// (on a mac hold down the command key to select multiple ranges)
if (rangySelection.rangeCount) {
var range = rangySelection.getRangeAt(0);
- var editableSelector = '.' + config.editableClass;
- var hostNode = $(range.commonAncestorContainer).closest(editableSelector);
- if (hostNode.length) {
- return new RangeContainer(hostNode[0], range);
+ var hostNode = parser.getHost(range.commonAncestorContainer);
+ if (hostNode) {
+ return new RangeContainer(hostNode, range);
}
}